Jenkins+Gradle+Python进行Android自动化打包

3,936 阅读6分钟
原文链接: www.jianshu.com

Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能。本文要讲的内容是通过Jenkins进行Android项目的自动化打包,通过Jenkins运行Gradle编译出apk文件后通过Python脚本上传apk到蒲公英并且上传完成后发送通知邮件


环境:Windows10+Python3.6+Java1.8 软件:Jenkins2.89.3+Git2.13.2

一.安装Jenkins

1.安装文件

先从Jenkis官网下载页面下载对应的安装包。下面以Windows环境为例。


下载成功后安装对应的msi文件,可以直接安装。
安装完成后,确保在环境变量中已经配置了JAVA_HOME,找到jenkins安装目录,运行命令:
java -jar jenkins.war 

Jenkins的默认端口是8080,如果要换端口号可以执行以下命令(以端口号是9090为例),

java -jar jenkins.war  --ajp13Port=-1 --httpPort=9090

下面可以看到Jenkins的启动信息



启动完成后在浏览器中输入

http://localhost:9090

第一次打开会看到需要填写管理员密码,密码所在文件的路径在网页上已经显示了,打开文本复制密码填写到输入框中点击下一步按钮即可。



2.安装插件

初次打开页面后Jenkins会初始化一段时间,可以先跳过插件安装,因为下载插件实在太慢,后面在[系统管理]中的[管理插件]选择需要的插件。


在可选插件中选择Git Plugin 和 Gradle Plugin,然后点击最底下“直接安装”按钮进行安装,安装完成后需要重新启动Jenkins。

当然直接选择系统推荐的插件配置进行安装也是可以的。


二.配置Jenkins

1.配置Android环境变量

如果还没有配置Android环境变量需要进行配置。建议在Windows系统中配置环境变量。当然也可以在Jenkins的[系统设置]
中进行配置。

2.配置Git

进入[全局工具配置],选择配置JDK。因为已经安装过JDK,所以只要填写名称和环境变量所对应的路径即可。

3.配置Git

进入[全局工具配置],选择配置Git。和JDK一样已经安装过,所以只配置路径。

3.配置Gradle

进入[全局工具配置],选择配置Gradle。这里选择自动安装,选择对应的Gradle版本号。

三.创建项目

1.创建

新建一个任务,填写任务名称,选择“构建一个自由风格的软件项目”,点击“确定”。创建完成后会进入项目的[配置]界面。

2.配置“参数化构建过程”

Parameters allow you to prompt users for one or more inputs that will be passed into a build. For example, you might have a project that runs tests on demand by allowing users to upload a zip file with binaries to be tested. This could be done by adding a File Parameter here.
Or you might have a project that releases some software, and you want users to enter release notes that will be uploaded along with the software. This could be done by adding a Multi-line String Parameter here.
Each parameter has a Name and some sort of Value, depending on the parameter type. These name-value pairs will be exported as environment variables when the build starts, allowing subsequent parts of the build configuration (such as build steps) to access those values, e.g. by using the ${PARAMETER_NAME} syntax (or %PARAMETER_NAME% on Windows).
This also implies that each parameter defined here should have a unique Name.

上面是官方的解释。本文将这些添加的参数命名为“构建参数”。构建参数的作用相当于这个构件任务的一个全局变量,可以选择如布尔值、String值等不同类型。在你启动构件任务时可以将这些参数传递过去, 并且在任务中通过 ${PARAMETER_NAME} 或者 %PARAMETER_NAME% 的格式来引用这些参数。

比方说,启动任务时你要能够选择拉取不同分支的代码,那么创建一个Choice类型的构造参数,命名为Branch,可选择的参数就是你Git仓库里的分支名称。例如:master、sprint1、sprint2。当然你也可以创建渠道apk需要打包的渠道名称或者环境名等等。



当配置构件参数以后,就会在启动任务时提供选择参数的选项,选择具体的运行方案。


3.配置Git仓库地址

在源码管理中填写你的项目地址,如果项目需要密码,则填写项目需要授权的帐号和密码。之前没有添加过的话点击“Add”按钮进行添加。上一步创建的构建参数也可以使用到了。配置分支需要拉取代码的分支为

*/${BRANCH}
4.构建配置

因为是使用Gradle打包,所以在构建中直接选择“invoke Gradle script”



然后选择先前安装的Gradle配置



在Task中填写以下命令。
clean assemble${FLAVORS}${BUILD_TYPE} --stacktrace --debug

其中${FLAVORS}和${BUILD_TYPE}的含义是对应的渠道和创建类型。与Android工程中build.gradle文件的配置中productFlavors 和buildTypes 的相对应。如果不需要设置相应的buildTypes和productFlavors等配置,把assemble${FLAVORS}${BUILD_TYPE}替换为assembleDebug命令就可以了。

buildTypes {
    release {
        //debuggable = true;
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.release
    }
    beta {
        debuggable = true;
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.release
    }
    qa {
        debuggable = true;
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.release
    }
}
productFlavors {
    northAmerica {
        applicationId 'xxx'
    }
    china {
        applicationId 'xxx'
    }

}
5.上传蒲公英配置

上传蒲公英的代码使用是Python脚本来完成。这里只放重要的部分,全部代码可以在Demo中下载。另外也可以查看官方文档,通过Jenkins插件来上传apk到蒲公英。因为考虑到灵活性的问题,如果以后要上传到公司的服务器或者别的需求修改起来也方便,还是决定通过python脚本调用官方提供的 HTTP接口来上传apk。

# Jenkins中Android项目的工作空间目录
workspace = "C:\\Users\\Flyn\\.jenkins\\workspace\\Android App"
# Gradle编译完成生成apk所在的目录
apk_path = workspace + "\\app\\build\outputs\\apk"

BRANCH = sys.argv[1]  # 参数
BUILD_TYPE = sys.argv[2]  # 创建类型
FLAVORS = sys.argv[3]  # 渠道

def upload_file(path):
    parents = os.listdir(path)
    for parent in parents:
        child = os.path.join(path, parent)
        if os.path.isdir(child):
            upload_file(child)
        else:
            if child.endswith(".apk"):
                print(child)
                # Http上传apk文件,官方文档:https://www.pgyer.com/doc/view/api#uploadApp
                result = requests.post(
                    url="https://www.pgyer.com/apiv2/app/upload",
                    data={"_api_key": "8bc2faf1cea2b0aa0b18465fd3b7ed47",
                          },
                    files={"file": open(child, "rb")}
                )
                if result.status_code < 300:
                    response = result.text
                    print(response)
                    # 拼接下载apk的链接
                    url = "https://www.pgyer.com/" + json.loads(response)['data'][
                        'buildShortcutUrl']
                    print("apk 文件上传成功 链接为:" + url)
                    # 上传成功后发送邮件,发送内容格式为文本
                    mail_sender.send(
                        "[" + date + "] Android APP " + BRANCH + " " + FLAVORS + " " + BUILD_TYPE + " APK 下载文件 ",
                        "APK编译时间:" + date + "\n" +
                        "APK 下载链接:" + url,
                        lambda: print("发送邮件成功!!"),
                        lambda: print("发送邮件失败!!")
                    )
                else:
                    print("apk 文件上传到蒲公英失败!!")

因为是python脚本,所以需要用windows批处理来执行脚本。选择“Execute Windows batch command”。



填写上windows执行命令,将下面代码中的路径换成你自己的python脚本所在的路径

py -3 C:\User\Development\my_scripts\APK_upload_scripts\apk_upload.py  %BRANCH%  %BUILD_TYPE%  %FLAVORS%

6.发送邮件配置
发送邮件依旧使用python脚本,不过也可以使用Jenkins自带的邮件通知或者Email Extension Plugin插件。因为自带的邮件功能还是有缺陷,比如无法自定义邮件内容和格式。使用Email Extension Plugin插件可以参考这篇文章Jenkins 配置邮件通知,不过还是觉得比较繁琐,使用python脚本可以进行快速配置也可以实现发送邮件的功能。
下面是发送邮件的代码
创建mail_sender.py文件

from email.mime.text import MIMEText
import smtplib
from email.header import Header

class MailSender(object):
    def __init__(self):
        self.my_name = ''
        self.from_address = ''
        self.password = ''
        self.to_address = ''
        self.smtp_server = ''
        self.smtp_server_port = ''

    def send(self, title: str, content: str, success_callback, failed_callback):
        msg = MIMEText(content, 'plain', 'utf-8')
        from_address = self.my_name + '<' + self.from_address + '>'
        msg['From'] = from_address
        msg['To'] = ", ".join(self.to_address)
        msg['Subject'] = Header(title, 'utf-8')

        server = smtplib.SMTP(self.smtp_server, self.smtp_server_port)
        server.set_debuglevel(1)
        try:
            server.login(self.from_address, self.password)
            server.sendmail(self.from_address, self.to_address, msg.as_string())
            success_callback()
        except Exception as e:
            print(e)
            failed_callback(str(e.args))
        finally:
            server.quit()

创建config.txt文件,通过读取文件中配置来获得发送邮件的一些信息

[Info]
my_name = Your name
from_address = your mail
password = your password
to_address = addressee 
smtp_server = email server address
smtp_server_port = email server port 

调用发送邮件的方法

mail_sender = MailSender()
configparser = configparser.ConfigParser()
configparser.read(os.path.dirname(os.path.realpath(__file__)) + '\\config.txt')
mail_sender.to_address = configparser.get("Info", "to_address").split(',')
mail_sender.from_address = configparser.get("Info", "from_address")
mail_sender.my_name = configparser.get("Info", "my_name")
mail_sender.password = configparser.get("Info", "password")
mail_sender.smtp_server = configparser.get("Info", "smtp_server")
mail_sender.smtp_server_port = configparser.get("Info", "smtp_server_port")

# 上传成功后发送邮件,发送内容格式为文本
mail_sender.send(
    "[" + date + "] Android APP " + BRANCH + " " + FLAVORS + " " + BUILD_TYPE + " APK 下载文件 ",
    "APK编译时间:" + date + "\n" +
    "APK 下载链接:" + url,
    lambda: print("发送邮件成功!!"),
    lambda: print("发送邮件失败!!")
)

三.运行任务

接下来点击“Build with Paramerters”按钮,选择构建参数进行构建。



点击编号,再点击Consloe log 可以看到具体的日志信息




接收到的邮件

四.总结

使用Jenkins持续集成并不难,操作一步一步来注意细节就不容易会出错。其他参考的文章也很多,遇到问题也很容易解决。
附:项目代码