解放双手——Android的自动化构建及发布

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 在一个App从开发到测试的过程中,我有很长一段时间都是这样做的:打包,上传到tower,在tower上编写本次更新说明,通知测试。一般情况下,打包及上传的过程大概也就2分钟。除此之外,由于项目代码有作混淆,并且使用了bugly,因此在发出每个版本之后还需要将混淆的mapping.txt传到bugly上。

在一个App从开发到测试的过程中,我有很长一段时间都是这样做的:打包,上传到tower,在tower上编写本次更新说明,通知测试。一般情况下,打包及上传的过程大概也就2分钟。除此之外,由于项目代码有作混淆,并且使用了bugly,因此在发出每个版本之后还需要将混淆的mapping.txt传到bugly上。当日复一日,并且有时还遇到网络较差的情况时,这种人工手动的工作方式就很影响工作效率及心情了。因此,自动化构建及发布就成了必须掌握的技能了。
本篇分享的是我在Android自动化构建的一些经验,涉及到的工具及网站如下:
- Gradle
- fir.im
- Gitlab
- gitlab-ci-multi-runner

所述内容包含:
- 使用Gradle自动构建并发布到fir
- 使用Gitlab-CI,在提交时自动化构建并发布到fir
- 在服务器配置docker版的gitlab-ci-multi-runner
- 多flavor时,在fir上同时发布的解决方案

Gradle及fir带来的解放生产力

构建并上传apk到fir

我接触fir.im的时间比较早,那时官方就已经提供了一个命令行打包并上传的工具fir-cli。但是有两个问题是我难以忍受的:
1. 它需要安装,并且由于使用ruby编写,所以还需要ruby环境
2. 它会构建所有flavor的版本,虽然最后只上传一个(该问题后来已经解决)

于是,在发现它有提供API之后,我查阅了下Gradle的文档,自己写了一个简单的fir发布插件——fir-publish
这个插件很小很轻,没有使用额外依赖库,网络请求使用的也是Gradle本身就有的http-client的API。使用方式如下:
首先在根项目的build.gradle中加入以下依赖:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.githang:fir:0.1.6'
    }
}

然后在app里的build.gradle文件末尾加入以下配置:

apply plugin: 'fir'
fir {
    apiToken //fir.im上的API token
    bundleId android.defaultConfig.applicationId
    flavor "Test" (如果没有productFlavor,可不配置此项),仅在上传apk时需要
    appName 你的应用名称,仅在上传apk时需要
    icon 应用图标路径,仅在上传图标时需要
    changeLog "更新日志" // 或者使用 file("日志文件路径")
}

其中的API token在你登录fir之后,点击自己的账号就可以获取。

该插件向你的project添加以下4个任务:
- firCert 获取上传凭证
- firIcon 上传图标,依赖于firCert
- firApk 上传APK,依赖于firCert及assembleRelease(或assembleFlavorRelease)
- firAll 上传图标及APK,依赖于firIcon及firApk

在上面的配置中,更新日志可以直接写在上面,也可以单独创建一个更新日志的文件(推荐),每次要发布时只需要修改这个文件上的更新日志,然后执行以下命令即可自动构建并发布到fir:

./gradlew firAll

注:windows用户在前面不需要加./
这样,我们只需要让测试人员关注fir上的更新动态即可,而不必自己去等待构建完成再上传然后等待上传完成再去写更新日志。

构建时上传mapping.txt到bugly

关于自动上传mapping.txt到bugly的问题,其实bugly本身已有提供相关的Gradle插件bugly。但是它会在每次构建Release版本中都执行上传mapping.txt,而通常我们只是在最终打包版本给测试的时候才需要,所以我修改了一下配置:

def isPublish = hasProperty("publish")
bugly {
    appId = '你的appId'
    appKey = '你的appKey' 
    execute = isPublish 
    upload = isPublish
}

在这里新增加了一个变量 ,仅在有publish属性时才执行上传的命令,这个属性在执行的时候带入,因此我们打包并发布的命令将进一步演变为如下:

./gradlew firApk -Ppublish

Gitlab-CI带来的进一步解放

在上面的过程中,其实我们解决的最大问题是把构建——发布——编写版本更新日志这三个步骤合成一步,少去了中间过程的等待,但是结果还是我们要在每次需要时去手动执行这一步。
CI类的服务能够让我们把代码推送到服务器上时即可开始构建,使得我们的整个构建过程达到真正的自动化,而不用人工参与。

由于公司使用的是Gitlab,所以这里只谈Gitlab-CI相关的内容。

新版的Gitlab CI中使用的是gitlab-ci-multi-runner,关于它的安装可以到参考其官方文档,这里不再赘述。

需要注意一点的是,在安装之后进行注册时,如果你是想注册为共享runner(所有项目都可使用),那么第一个问题的地址应该是你们公司gitlab的地址,第二个问题的token在管理员界面的runner配置中可以看到。如果是想注册为私有的runner,则其url与token在项目的设置中可以看到。

接下来,只需要在我们的项目的根目录中添加一个.gitlab-ci.yml文件,并在其中进行CI配置,然后提交并推送到我们的gitlab上即可。
还是以我这里的公司项目为例。项目采用git-flow流程进行开发,在要发布时会创建release分支,因此需要发布到fir上给测试人员的是release分支及tag上的代码,其他分支的代码我们只需要进行构建测试就可以了。在我们公司的项目中,有开发环境 、测试环境及生产环境,分别对应三个productFlavor:Develop, Test,Official,它们之间只有API的地址不同。因此,构建测试使用其中一个环境的就可以了。
所以脚本如下:

before_script:
  - chmod +x ./gradlew

compileTest:
  script: "./gradlew clean aDevelopDebug"
  except:
    - /^release.*$/
    - tags

publishToFir:
  script: "./gradlew clean firApk -Ppublish"
  type: deploy
  only:
    - /^release.*$/
    - tags

在这里,我定义了两个ci任务,分别是compileTest以及publishToFirscript表示该任务所执行的命令。except表示不对哪些分支进行构建。使用git-flow流程时,将发布的分支都是以release/xxx来命名,所以这里用正则来表示。only表示仅对哪些分支执行这个构建任务。type表示任务的类型。

将配置提交,然后推送到Gitlab上,就能够触发CI去执行我们所定义的构建任务了。如果你成功了配置了Gitlab上的邮箱发送服务,那么我们就可以不用主动去关心这个结果,因为如果构建失败了,Gitlab将会向我们发送邮件通知。
构建失败时的邮件通知

如果你不想使用docker来运行runner,可跳过下面这一节。
如果你也不需要同时在fir上发布不同flavor的APK,那么后面的也不用看了。

更高级的Docker版的CI Runner

上面虽然使用了gitlab-ci-multi-runner来完成自动化,但是它是在我本机上跑的。每次编译时占用的内存及CPU会对开发略有影响,并且还需要我在每次开机后开个终端运行一下这个runner。公司内部是有一台Ubuntu服务器专门用于代码及项目相目的服务的,如果把我们的runner部署到这台服务器上那就更好了。
公司的这台服务器安装了Docker,其他的服务都是以docker形式运行的。既然这样,我也遵守规则用docker部署上runner吧。
向公司的技术大伽问来内部服务器的管理员账号,又翻了一遍《Docker — 从入门到实践》的PDF,然后就开始写Dockerfile并在自己电脑上试验了。

在踩了ubuntu版本安装不了JDK8、挂载Android SDK目录、没有32位动态库导致Android SDK执行不了,以及中文乱码等坑之后,目前我的Dockerfile如下:

FROM ubuntu:15.04

MAINTAINER HuangHaohang <msdx.android@qq.com>

ENV ANDROID_HOME /android-sdk

RUN apt update && apt install -y openjdk-8-jdk curl

#如果遇到android-sdk里的命令无法执行,则需要安装32位的动态链接库。
RUN apt install -y libc6-i386 lib32stdc++6 lib32gcc1 lib32ncurses5 lib32z1

RUN curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.deb.sh | bash
RUN apt-get install -y gitlab-ci-multi-runner

# Ensure UTF-8 locale
#COPY locale /etc/default/locale
RUN locale-gen zh_CN.UTF-8 && \
DEBIAN_FRONTEND=noninteractive dpkg-reconfigure locales
RUN locale-gen zh_CN.UTF-8
ENV LANG zh_CN.UTF-8
ENV LANGUAGE zh_CN:zh
ENV LC_ALL zh_CN.UTF-8

注:后续如果有变化,将在我的github项目上更新,地址为:https://github.com/msdx/dockerfile/blob/master/gitlab-ci-multi-runner/Dockerfile

然后执行docker build创建docker镜像之后,运行docker时挂载上android sdk以及可读写的gradle缓存目录就可以了,其他问题包括runner的注册等可参见我这里的项目说明:https://github.com/msdx/dockerfile/tree/master/gitlab-ci-multi-runner 。由于篇幅原因,这里不作赘述。
我这里把android-sdk打包并通过ssh上传到了服务器,然后解压在了公司的用户目录的android-sdk下,并在该目录创建了一个文件夹GradleUserHome,用于放Gradle的缓存,最终启动这个docker的脚本如下:

#!/bin/bash
sudo docker run -d \
  --name gitlab-ci-multi-runner \
  --restart always \
  -v /home/irain/android-sdk:/android-sdk:ro \
  -v /home/irain/GradleUserHome:/root/.gradle:rw \
  irain/gitlab-runner:registered \
  gitlab-ci-multi-runner --debug run

fir上的小技巧——多flavor的发布方式

前面提到我们公司的项目API地址是有分多个环境的(开发环境、测试环境以及生产环境)。本来我只需要打包测试环境的给测试人员用,生产环境在最终要发布的时候再自己打包。但是在这次新版本的开发中,服务端的人员也希望我能够打包开发环境的Apk给他,这样有时候他也可以自测一下。由于项目正在开发中,版本变化较快,所以我也想到通过自动构建发布到fir上,再由他自己去下载,这样就可保证他可以获得最新开发的版本。

然而,相当沮丧的一点是,fir上并不支持同一个应用多环境的发布,虽然这个需求在一年以前就有其他人提出。我问了客服,客服的建议是换一个bundleId(applicationId),当然这是不可能的,因为我们的应用使用到了高德地图、微信分享及各种支付等许多和applicationId关联的SDK,不可能重新部署一套。最后查看其他人分享的一个实现技巧。

首先,你需要在fir上注册一个号(当然你也可以请你同事帮忙),然后把你的应用上传上去,再进入应用的权限控制,把你的大号邀请进来,这样你的大号上就有两个这样的应用了,并且可以对它上传新版本来更新。当然,如果你使用API来上传,则不需要邀请,只需要填不同的API Token即可。所以最终,我的app的build.gradle中关于fir发布的配置如下:

def envFlavor = hasProperty("flavor") ? getProperty("flavor") : "Test"
if (envFlavor == "Develop") {
    fir {
        apiToken "小号的api token"
        bundleId android.defaultConfig.applicationId
        flavor envFlavor
        appName "XXX-开发版"
        changeLog "git show -s --format=%B HEAD".execute().text
    }
} else {
    fir {
        apiToken "大号的api token"
        bundleId android.defaultConfig.applicationId
        flavor envFlavor
        appName "XXX-测试版"
        changeLog file("./changeLog.txt")
    }
}

其中,flavor是通过定义的envFlavor来设置,而envFlavor根据执行的时候传入的flavor属性的值来设置。对应的.gitlab-ci.yml也修改如下:

before_script:
  - chmod +x ./gradlew

compileTest:
  script: "./gradlew clean  firApk -Ppublish -Pflavor=Develop"
  except:
    - /^release.*$/
    - tags

publishToFir:
  script: "./gradlew clean  firApk -Ppublish"
  type: deploy
  only:
    - /^release.*$/
    - tags
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
1月前
|
监控 jenkins 测试技术
自动化测试框架的构建与实践
【10月更文挑战第40天】在软件开发周期中,测试环节扮演着至关重要的角色。本文将引导你了解如何构建一个高效的自动化测试框架,并深入探讨其设计原则、实现方法及维护策略。通过实际代码示例和清晰的步骤说明,我们将一起探索如何确保软件质量,同时提升开发效率。
44 1
|
1月前
|
测试技术 开发者 Python
自动化测试之美:从零构建你的软件质量防线
【10月更文挑战第34天】在数字化时代的浪潮中,软件成为我们生活和工作不可或缺的一部分。然而,随着软件复杂性的增加,如何保证其质量和稳定性成为开发者面临的一大挑战。自动化测试,作为现代软件开发过程中的关键实践,不仅提高了测试效率,还确保了软件产品的质量。本文将深入浅出地介绍自动化测试的概念、重要性以及实施步骤,带领读者从零基础开始,一步步构建起属于自己的软件质量防线。通过具体实例,我们将探索如何有效地设计和执行自动化测试脚本,最终实现软件开发流程的优化和产品质量的提升。无论你是软件开发新手,还是希望提高项目质量的资深开发者,这篇文章都将为你提供宝贵的指导和启示。
|
2月前
|
安全 Android开发 iOS开发
Android vs. iOS:构建生态差异与技术较量的深度剖析###
本文深入探讨了Android与iOS两大移动操作系统在构建生态系统上的差异,揭示了它们各自的技术优势及面临的挑战。通过对比分析两者的开放性、用户体验、安全性及市场策略,本文旨在揭示这些差异如何塑造了当今智能手机市场的竞争格局,为开发者和用户提供决策参考。 ###
|
23天前
|
人工智能 自然语言处理 JavaScript
Agent-E:基于 AutoGen 代理框架构建的 AI 浏览器自动化系统
Agent-E 是一个基于 AutoGen 代理框架构建的智能自动化系统,专注于浏览器内的自动化操作。它能够执行多种复杂任务,如填写表单、搜索和排序电商产品、定位网页内容等,从而提高在线效率,减少重复劳动。本文将详细介绍 Agent-E 的功能、技术原理以及如何运行该系统。
73 5
Agent-E:基于 AutoGen 代理框架构建的 AI 浏览器自动化系统
|
8天前
|
Serverless 决策智能 UED
构建全天候自动化智能导购助手:从部署者的视角审视Multi-Agent架构解决方案
在构建基于多代理系统(Multi-Agent System, MAS)的智能导购助手过程中,作为部署者,我体验到了从初步接触到深入理解再到实际应用的一系列步骤。整个部署过程得到了充分的引导和支持,文档详尽全面,使得部署顺利完成,未遇到明显的报错或异常情况。尽管初次尝试时对某些复杂配置环节需反复确认,但整体流程顺畅。
|
13天前
|
缓存 监控 安全
公司电脑监控软件的 Gradle 构建自动化优势
在数字化办公环境中,公司电脑监控软件面临代码更新频繁、依赖管理和构建复杂等挑战。Gradle 构建自动化工具以其强大的依赖管理、灵活的构建脚本定制及高效的构建缓存与增量构建特性,显著提升了软件开发效率和质量,支持软件的持续更新与优化,满足企业对员工电脑使用情况的监控与管理需求。
26 3
|
25天前
|
数据库 Docker 容器
Docker在现代软件开发中扮演着重要角色,通过Dockerfile自动化构建Docker镜像,实现高效、可重复的构建过程。
Docker在现代软件开发中扮演着重要角色,通过Dockerfile自动化构建Docker镜像,实现高效、可重复的构建过程。Dockerfile定义了构建镜像所需的所有指令,包括基础镜像选择、软件安装、文件复制等,极大提高了开发和部署的灵活性与一致性。掌握Dockerfile的编写,对于提升软件开发效率和环境管理具有重要意义。
48 9
|
23天前
|
运维 jenkins Java
Jenkins 自动化局域网管控软件构建与部署流程
在企业局域网管理中,Jenkins 作为自动化工具,通过配置源码管理、构建及部署步骤,实现了高效、稳定的软件开发与部署流程,显著提升局域网管控软件的开发与运维效率。
39 5
|
1月前
|
jenkins 测试技术 持续交付
自动化测试框架的构建与优化:提升软件交付效率的关键####
本文深入探讨了自动化测试框架的核心价值,通过对比传统手工测试方法的局限性,揭示了自动化测试在现代软件开发生命周期中的重要性。不同于常规摘要仅概述内容,本部分强调了自动化测试如何显著提高测试覆盖率、缩短测试周期、降低人力成本,并促进持续集成/持续部署(CI/CD)流程的实施,最终实现软件质量和开发效率的双重飞跃。通过具体案例分析,展示了从零开始构建自动化测试框架的策略与最佳实践,包括选择合适的工具、设计高效的测试用例结构、以及如何进行性能调优等关键步骤。此外,还讨论了在实施过程中可能遇到的挑战及应对策略,为读者提供了一套可操作的优化指南。 ####
|
1月前
|
敏捷开发 监控 测试技术
探索自动化测试框架的构建与优化####
在软件开发周期中,自动化测试扮演着至关重要的角色。本文旨在深入探讨如何构建高效的自动化测试框架,并分享一系列实用策略以提升测试效率和质量。我们将从框架选型、结构设计、工具集成、持续集成/持续部署(CI/CD)、以及最佳实践等多个维度进行阐述,为软件测试人员提供一套系统化的实施指南。 ####