当前位置: 首页 > news >正文

Android配置GitLab CI/CD持续集成,Shell版本的gitlab-runner,FastLane执行,上传蒲公英

mac环境下,

首选需要安装gitlab-runner和fastlane

brew install gitlab-runner

brew install fastlane

安装完成,来到我们在gitlab下新建的Android项目,我们开始创建gitlab-runner

1、创建runner

点开runner,点击新建runner

选择macos,自定义一个标签,把运行未打标签的作业也够选上,点击创建runner

然后来到这个页面,开始在终端挨个执行命令

执行命令,一定要选择shell,可以看到这时候就创建runner成功了

testrunner就是这个runner的名字

gitlab-runner list  

执行这个命令可以看到电脑上所有的runner

gitlab-runner status  可以查看gitlab-runner的状态

如果状态不在线,可以调用gitlab-runner  install 命令  再调用gitlab-runner  start

2、fastlane

cd到项目跟目录下,使用fastlane init

根目录下就会创建fastlane文件夹

我们要写的代码都在Fastfile中

# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
#     https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
#     https://docs.fastlane.tools/plugins/available-plugins
## Uncomment the line if you want fastlane to automatically update itself
# update_fastlanedefault_platform(:android)
APK_TIME = Time.now.strftime("%Y%m%d")
app_versionName  = "1.0.0"BUILD_NAME = ""
# OUTPUT_NAME_PATCH = "#{BUILD_NAME}_#{PLIST_INFO_VERSION}_#{APK_TIME}"
platform :android dodesc "给测试用单渠道cnrmall_official的包"puts "APK_TIME=== #{APK_TIME}"channel = "cnrmall_official"lane :debug_cnrmall_official doBUILD_NAME = "android_Debug_#{channel}"gradle(task: "clean assemble#{channel}",build_type: "Debug")mk_cp_apkpgyer_upload(channel: "#{channel}")end#debug全渠道包
lane :debug dochannel = "cnrmall_official"BUILD_NAME = "android_Debug"gradle(task: "clean assemble",build_type: "Debug")pgyer_upload(channel: "#{channel}")end#release全渠道包lane :release dochannel = "cnrmall_official"BUILD_NAME = "android_Release"gradle(task: "clean assemble",build_type: "Release")pgyer_upload(channel: "#{channel}")end# 执行创建文件夹 copy apk 到最外面目录
lane :mk_cp_apk doputs "mk out_apk cp apk"gradle(task: "customBuild")endlane :get_version doputs "Update Android version in build.gradle"app_version = gradle(task: "getVersionName")# 获取版本号
#       puts "app_version:#{versionCode}"enddesc "Submit a new Beta Build to Crashlytics Beta"lane :beta dogradle(task: "clean assembleRelease")crashlytics# sh "your_script.sh"# You can also use other beta testing services hereenddesc "Deploy a new version to the Google Play"lane :deploy dogradle(task: "clean assembleRelease")upload_to_play_storeend##上传蒲公英lane :pgyer_upload do |option|sh 'ls'pyg_get_channel = option[:channel]puts "上传蒲公英channel= #{pyg_get_channel}"
#      ENV['outPutPath'] = '../app/build/outputs/apk/'+"#{pyg_get_channel}"+"/debug/cnrmall_"+"#{APK_TIME}"+"_"+"#{app_versionName}"+"_"+"#{pyg_get_channel}"+".apk"ENV['outPutPath'] = '../out_apk/cnrmall_'+"#{APK_TIME}"+"_"+"#{app_versionName}"+"_"+"#{pyg_get_channel}"+".apk"puts "outPutPath路径- #{ENV['outPutPath']}"
#      ENV['outPutPath'] = '../app/build/outputs/apk/option/debug/cnrmall_#{APK_TIME}_1.0.0_cnrmall_official.apk'
#      sh "open ../app/build/outputs/apk/debug/"sh "./pgyer_upload.sh  -k 4df7384110457be3f5bce0c391ef1cd3  #{ENV['outPutPath']} "endend

上传蒲公英脚本

#!/bin/bash
#
# 通过shell脚本来实现将本地app文件通过API上传到蒲公英
# https://www.pgyer.com/doc/view/api#fastUploadApp
## Display log. 1=enable, 0=disable
LOG_ENABLE=1printHelp() {echo "Usage: $0 -k <api_key> [OPTION]... file"echo "Upload iOS or Android app package file to PGYER."echo "Example: $0 -k xxxxxxxxxxxxxxx /data/app.apk"echo ""echo "Description:"echo "  -k api_key                       (required) api key from PGYER"echo "  -t buildInstallType              build install type, 1=public, 2=password, 3=invite"echo "  -p buildPassword                 build password, required if buildInstallType=2"echo "  -d buildUpdateDescription        build update description"echo "  -e buildInstallDate              build install date, 1=buildInstallStartDate~buildInstallEndDate, 2=forever"echo "  -s buildInstallStartDate         build install start date, format: yyyy-MM-dd"echo "  -e buildInstallEndDate           build install end date, format: yyyy-MM-dd"echo "  -c buildChannelShortcut          build channel shortcut"echo "  -h help                          show this help"echo ""echo "Report bugs to: <https://github.com/PGYER/pgyer_api_example/issues>" echo "Project home page: <https://github.com/PGYER/pgyer_api_example>" exit 1
}while getopts 'k:t:p:d:s:e:c:h' OPT; docase $OPT ink) api_key="$OPTARG";;t) buildInstallType="$OPTARG";;p) buildPassword="$OPTARG";;d) buildUpdateDescription="$OPTARG";;e) buildInstallDate="$OPTARG";;s) buildInstallStartDate="$OPTARG";;e) buildInstallEndDate="$OPTARG";;c) buildChannelShortcut="$OPTARG";;?) printHelp;;esac
doneshift $(($OPTIND - 1))
readonly file=$1# check api_key exists
if [ -z "$api_key" ]; thenecho "api_key is empty"printHelp
fi# check file exists
if [ ! -f "$file" ]; thenecho "file not exists"printHelp
fi# check ext supported
buildType=${file##*.}
if [ "$buildType" != "" ] && [ "$buildType" != "apk" ]; thenecho "file ext is not supported"printHelp
fi# ---------------------------------------------------------------
# functions
# ---------------------------------------------------------------log() {[ $LOG_ENABLE -eq 1 ]  && echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*"
}logTitle() {log "-------------------------------- $* --------------------------------"
}execCommand() {log "$@"result=$(eval $@)
}# ---------------------------------------------------------------
# 获取上传凭证
# ---------------------------------------------------------------logTitle "获取凭证"command="curl -s"
[ -n "$api_key" ]                && command="${command} --form-string '_api_key=${api_key}'";
[ -n "$buildType" ]              && command="${command} --form-string 'buildType=${buildType}'";
[ -n "$buildInstallType" ]       && command="${command} --form-string 'buildInstallType=${buildInstallType}'";
[ -n "$buildPassword" ]          && command="${command} --form-string 'buildPassword=${buildPassword}'";
[ -n "$buildUpdateDescription" ] && command="${command} --form-string $'buildUpdateDescription=${buildUpdateDescription}'";
[ -n "$buildInstallDate" ]       && command="${command} --form-string 'buildInstallDate=${buildInstallDate}'";
[ -n "$buildInstallStartDate" ]  && command="${command} --form-string 'buildInstallStartDate=${buildInstallStartDate}'";
[ -n "$buildInstallEndDate" ]    && command="${command} --form-string 'buildInstallEndDate=${buildInstallEndDate}'";
[ -n "$buildChannelShortcut" ]   && command="${command} --form-string 'buildChannelShortcut=${buildChannelShortcut}'";
command="${command} http://www.pgyer.com/apiv2/app/getCOSToken";
execCommand $command[[ "${result}" =~ \"endpoint\":\"([\:\_\.\/\\A-Za-z0-9\-]+)\" ]] && endpoint=`echo ${BASH_REMATCH[1]} | sed 's!\\\/!/!g'`
[[ "${result}" =~ \"key\":\"([\.a-z0-9]+)\" ]] && key=`echo ${BASH_REMATCH[1]}`
[[ "${result}" =~ \"signature\":\"([\=\&\_\;A-Za-z0-9\-]+)\" ]] && signature=`echo ${BASH_REMATCH[1]}`
[[ "${result}" =~ \"x-cos-security-token\":\"([\_A-Za-z0-9\-]+)\" ]] && x_cos_security_token=`echo ${BASH_REMATCH[1]}`if [ -z "$key" ] || [ -z "$signature" ] || [ -z "$x_cos_security_token" ] || [ -z "$endpoint" ]; thenlog "get upload token failed"exit 1
fi# ---------------------------------------------------------------
# 上传文件
# ---------------------------------------------------------------logTitle "上传文件"file_name=${file##*/}execCommand "curl --progress-bar -o /dev/null -w '%{http_code}' \
--form-string 'key=${key}' \
--form-string 'signature=${signature}' \
--form-string 'x-cos-security-token=${x_cos_security_token}' \
--form-string 'x-cos-meta-file-name=${file_name}' \
-F 'file=@${file}' ${endpoint}"
if [ $result -ne 204 ]; then # if http code != 204, upload failedlog "Upload failed"exit 1
fi# ---------------------------------------------------------------
# 检查结果
# ---------------------------------------------------------------logTitle "检查结果"# 获取 .apk 文件所在的目录
Directory=$(dirname "$file")echo "获取 .apk 文件所在的目录 $Directory"jsonFilePath="$Directory/result.json"
qrCodeFilePath="$Directory/qr_code.png"for i in {1..60}; doexecCommand "curl -s http://www.pgyer.com/apiv2/app/buildInfo?_api_key=${api_key}\&buildKey=${key}"[[ "${result}" =~ \"code\":([0-9]+) ]] && code=`echo ${BASH_REMATCH[1]}`if [ $code -eq 0 ]; thenecho $resultecho $result > "$jsonFilePath"  # 将 JSON 保存到文件# 提取 buildQRCodeURL 的值
#        qrCodeURL=$(jq -r '.data.buildQRCodeURL' <<< "$result")qrCodeURL=$(echo "$result" | grep -o '"buildQRCodeURL":"[^"]*' | cut -d '"' -f 4 | sed 's/\\//g')# 使用 curl 下载并保存 QR Code 文件echo "Downloading QR Code from: $qrCodeURL"#         curl -s "$qrCodeURL" -o "$qrCodeFilePath"curl --location-trusted -o "$qrCodeFilePath" "$qrCodeURL"echo "curl Result: $curlResult"breakelsesleep 1fi
done

.gitlab-ci.yml,只需要写上执行哪个fastlane即可 ,debug_cnrmall_official是fastlane名字

在Android studio中的Terminal中,也可以使用命令去执行某一个fastlane

如:fastlane lane:debug_cnrmall_official

stages:- buildjob:stage: buildscript:- echo "开始打包4"- ls -a# - ./build.sh- fastlane debug_cnrmall_officialartifacts:paths:- out_apk/*

项目跟目录的build.gradle文件中,还自定义了一个task

因为Android默认打包完的apk位置太深了,所以把它复制到外面一点,方便测试拿到

cnrmallshop_ci_2.0是项目名 out_apk是创建的文件夹
task customBuild(type: Exec) {def date = new SimpleDateFormat("yyyyMMdd").format(new Date())def versionName = rootProject.android.versionNamecommandLine 'sh','-c','mkdir -p ../cnrmallshop_ci_2.0/out_apk && cp app/build/outputs/apk/cnrmall_official/debug/cnrmall_'+date+'_'+versionName+'_cnrmall_official.apk ../cnrmallshop_ci_2.0/out_apk/cnrmall_'+date+'_'+versionName+'_cnrmall_official.apk'
}

最后可以在流水线上中看到构建成功的流水线,可以点击下载按钮下载产物

​​​​​​​

产物中有apk有二维码

相关文章:

Android配置GitLab CI/CD持续集成,Shell版本的gitlab-runner,FastLane执行,上传蒲公英

mac环境下&#xff0c; 首选需要安装gitlab-runner和fastlane brew install gitlab-runner brew install fastlane 安装完成&#xff0c;来到我们在gitlab下新建的Android项目&#xff0c;我们开始创建gitlab-runner 1、创建runner 点开runner&#xff0c;点击新建runner …...

算法提升——LeetCode383场周赛总结

周赛题目 边界上的蚂蚁 边界上有一只蚂蚁&#xff0c;它有时向左走&#xff0c;有时向右走。 给你一个非零整数数组nums。蚂蚁会按顺序读取nums中的元素&#xff0c;从第一个元素开始直到结束。每一步&#xff0c;蚂蚁会根据当前元素的值移动&#xff1a; 如果nums[i]<0…...

(delphi11最新学习资料) Object Pascal 学习笔记---第4章第2.1节( 带结果的Exit例程)

4.2.1 带结果的Exit例程 ​ 我们已经看到&#xff0c;从函数中返回结果所使用的语法与 C 语言家族的语法截然不同。不仅语法不同&#xff0c;行为也不同。为结果&#xff08;或函数名&#xff09;赋值并不像return语句那样终止函数。Object Pascal 开发人员经常利用这一特性&a…...

vuecli3 执行 npm run build 打包命令报错:TypeError: file.split is not a function

问题 今天有个项目在打包的时候遇到了一个问题&#xff0c;就是执行 npm run build 命令的时候报错了&#xff0c;如下&#xff1a; 解决 我排查了一下&#xff0c;模拟代码如下&#xff1a;在打包的时候用了 MinChunkSizePlugin const webpack require("webpack"…...

【Java 数据结构】对象的比较

Java中对象的比较 1. PriorityQueue中插入对象2. 元素的比较2.1 基本类型的比较2.2 对象比较的问题 3. 对象的比较3.1 覆写基类的equals3.2 基于Comparble接口类的比较3.3 基于比较器比较3.4 三种方式对比 4. 集合框架中PriorityQueue的比较方式5. 使用PriorityQueue创建大小堆…...

2024 Google Chrome 浏览器回退安装旧版本

2024 Google Chrome 浏览器回退安装旧版本 查看当前谷歌版本备份浏览器数据卸载浏览器双击重新安装旧版本浏览器 查看当前谷歌版本 详细参考&#xff1a;参考 笔记&#xff1a;最近谷歌浏览器更新后&#xff0c;用着总感觉别扭&#xff1a;不习惯 备份浏览器数据 &#xff…...

将数组中的各字符串都调整为指定长度调整原则:多删(删右侧多出的)少补(左侧补数字0)numpy.char.zfill()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 将数组中的各字符串 都调整为指定长度 调整原则&#xff1a; 多删&#xff08;删右侧多出的&#xff09; 少补&#xff08;左侧补数字0&#xff09; numpy.char.zfill() [太阳]选择题 请问以…...

算法题目题单——图论

简介 本文为自己做的一部分图论题目&#xff0c;作为题单列出&#xff0c;持续更新。 题单由题目链接和题解两部分组成&#xff0c;题解部分提供简洁题意&#xff0c;代码仓库&#xff1a;Kaiser-Yang/OJProblems。 对于同一个一级标题下的题目&#xff0c;题目难度尽可能做…...

Maven提示Failure to find com.oracle:ojdbc14:jar:10.2.0.4.0

目录 问题 解决方案 1、下载oracle的驱动jar包 2、安装到本地仓库 3、检查本地仓库是否成功安装 4、Maven先clean &#xff0c;再install。 问题 项目引入Oracle依赖后报错&#xff0c;显示为红色。 解决方案 1、下载oracle的驱动jar包 首先我们要去下载一个oracle的…...

深度学习的数据集制作、标注、处理相关软件

制作深度学习数据集通常涉及数据的采集、标注和预处理等步骤。以下是一些可用于制作和处理深度学习数据集的软件工具&#xff0c;以及它们的详细介绍&#xff1a; 数据采集和生成 Web爬虫工具 (如 Scrapy, Beautiful Soup) 描述&#xff1a;这些工具可以帮助你从网上自动抓取和…...

点击按钮打开自定义iframe弹窗

1、效果 点击按钮打开弹窗&#xff1a; 打开弹窗后&#xff1a; 2、代码 <!DOCTYPE html> <html><head><title>iframe弹窗</title><style>/* 使用媒体查询来实现响应式设计 */media (min-width: 768px) {.popup {width: 80%; /* 设置…...

LeetCode977 有序数组的平方

暴力解法是平方之后排序复杂度是nnlogn 优化解法是双指针i&#xff0c;j&#xff0c;i放数组首元素位置&#xff0c;j放数组末尾&#xff0c;每次比较i和j位置的数组元素大小&#xff0c;然后挑一个大的放在新的数组元素的指定末尾位置上。 当原始数组nums第一个元素大于零时&a…...

Windows自动化实现:系统通知和任务栏图标自定义

文章目录 Windows自动化的三个小工具系统通知任务栏图标使用pystray实现使用infi.systray实现 Windows自动化的三个小工具 系统通知 import win10toastwin10toast.ToastNotifier().show_toast("eee", "休息一下", icon_path"icon.ico", durati…...

Spring | Spring的“数据库开发“ (Srping JDBC)

目录&#xff1a; Spring JDBC1.Spring JDBC的核心类 ( JdbcTemplate类 )2.Srping JDBC 的配置3.JdbcTemplate类的“常用方法”execute( )&#xff1a;直接执行“sql语句”&#xff0c;没有返回值update( ) &#xff1a;“增删改”&#xff0c;返回 “影响的行数”query( ) : “…...

面试八股文(2)

文章目录 1.ArrayList和LinkedList区别2.HashMap和HashTable区别3.线程的创建方式4.Java中异常处理5.Java序列化中某些字段不想进行序列化&#xff1f;6.Java序列化7.静态方法和实例方法8.List、Set、Map三者区别9.ArrayList和Vector区别10.HashMap和HashSet区别 1.ArrayList和…...

记elasticsearch CPU负载100%问题

记elasticsearch CPU负载100%问题 环境&#xff1a;问题表现&#xff1a;初步排查&#xff1a;日志查询hot_thread 深入查询当前elasticsearch正在运行的Task查看Task详情解决问题对导致问题的原因的几个猜测问题复现&#xff1a;导致问题的原因。json导入规则问题json导入规则…...

回归预测 | Matlab实现OOA-CNN-LSTM-Attention鱼鹰算法优化卷积长短期记忆网络注意力多变量回归预测(SE注意力机制)

回归预测 | Matlab实现OOA-CNN-LSTM-Attention鱼鹰算法优化卷积长短期记忆网络注意力多变量回归预测&#xff08;SE注意力机制&#xff09; 目录 回归预测 | Matlab实现OOA-CNN-LSTM-Attention鱼鹰算法优化卷积长短期记忆网络注意力多变量回归预测&#xff08;SE注意力机制&…...

PyTorch、NCNN、CV::Mat三者张量的shape

目录 一、PyTorch二、NCNN三、CV::Mat 一、PyTorch 在 PyTorch 中&#xff0c;张量&#xff08;Tensor&#xff09;的形状通常按照 (N, C, H, W) 的顺序排列&#xff0c;其中&#xff1a; N 是批量大小&#xff08;batch size&#xff09; C 是通道数&#xff08;channel numb…...

社交平台内容创作未来会有哪些方向?

内容为王的时代下&#xff0c;企业如果想要通过社交平台占据用户心智&#xff0c;可以找到适合自己的内容营销策略&#xff0c;好的内容能够与消费者建立信任关系&#xff0c;今天 媒介盒子就来和大家聊聊&#xff1a;社交平台内容创作的方向。 一、 内容逐渐细分 相比于原来…...

MySQL温故篇(一)SQL语句基础

一、SQL语句基础 1、SQL语言分类 DDL&#xff1a;数据定义语言 DCL&#xff1a;数据控制语言 DML&#xff1a;数据操作语言 DQL&#xff1a;数据的查询语言 2、数据类型 3、字符类型 char(11) &#xff1a; 定长 的字符串类型,在存储字符串时&#xff0c;最大字符长度11个&a…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问&#xff08;基础概念问题&#xff09; 1. 请解释Spring框架的核心容器是什么&#xff1f;它在Spring中起到什么作用&#xff1f; Spring框架的核心容器是IoC容器&#…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...

Golang——6、指针和结构体

指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...

springboot 日志类切面,接口成功记录日志,失败不记录

springboot 日志类切面&#xff0c;接口成功记录日志&#xff0c;失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...

Python 高效图像帧提取与视频编码:实战指南

Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...

Modbus RTU与Modbus TCP详解指南

目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...