Android Jenkins CI集成

2018-11-13  本文已影响114人  PengFly

Android Jenkins CI集成

Jenkins是一个独立的基于Java的程序,随时可以运行,包含Windows,Mac OS X和其他类Unix操作系统的软件包。作为可扩展的自动化服务器,Jenkins可以用作简单的CI服务器,也可以用作任何项目的持续交付中心。

环境搭建

Jenins启动及初始化

  1. 启动java -jar jenkins.war --httpPort=8888

  2. 浏览器中通过http://127.0.0.1:8888访问Jenkins

  3. 使用cat命令查看密码,并使用admin账号进行登录

cat /home/dev/.jenkins/secrets/initialAdminPassword

系统在初始化时,会下载一些必要的插件,等待下载完成即可使用。

插件管理及系统设置

Jeninks提供了大量的插件,供开发人员使用。如果未找到合适的,可自己开发相应的开源插件。

Android CI需要如下插件(根据个人及公司需要自行选择):

新建任务及配置

主界面

主界面

选择【系统管理】-【系统设置】

系统设置】

进入系统设置,配置全局变量ANDROID_HOME、汉化、Jenkins Location信息填写

ANDROID_HOME、汉化、Jenkins Location信息填写

配置Git账号信息

配置Git账号信息

配置邮件通知

配置邮件通知 配置邮件通知

配置完成系统环境后,返回【系统管理】选择【全局工具配置】,指定JDK、Gradle及Git的环境信息

全局工具配置 全局工具配置

【系统设置】及【全局工具配置】完成后,就可以创建任务进行配置我们的打包项目了

创建任务

通用配置,选择参数化配置,参数化配置可选择10多种参数类型,但Git Parameter参数需要使用Git Parameter Plugins支持

主界面 主界面

【源码管理】这里指定项目的git地址以及用户名和密码,并指定需要拉取源码的分支路径,这里使用了master主分支

主界面

根据情况而定,如果使用了SSH登录则选择SSH username with private key(具体SSH配置自行百度)

主界面

【构建触发器】构建触发器可配置何时来触发Jenkins自动执行任务,有脚本,定时任务等多种配置

【构建环境】貌似没看到太大做用在此处

【构建】构建需要指定Gradle的版本如果在【全局工具配置】中加入后该处会显示,Tasks即在使用Gradle编译时的命令:


clean assemble${PRODUCT_FLAVORS}${BUILD_TYPE} --stacktrace --debug

clean:作用是在编译前清除原始build产物

assemble${PRODUCT_FLAVORS}${BUILD_TYPE}:会拼接成为一个buildTask,例如:assembleWandoujiaDebug,该任务可参考AS右侧Gradle-:app-build中的任务列表

构建

【构建后操作】构建后可选择使用邮件通知或webhook通知,这里使用了邮件通知+钉钉的Webhook机器人通知

构建后操作 构建后操作

注:钉钉的通知插件有2个目前【Dingding[钉钉]Plugin】【Dingding JSON Pusher Plugin】,后者是支持Json数据格式发送的即钉钉支持的所有消息格式,但是亲测不生效。也不知道是自己配置错误的问题。第一个则支持的其实是link消息格式。但是Link消息格式不知道如何指定@人,所以这里使用了另一种解决方法,使用[shell curl]命令进行网络请求测试发送(详见上图【构建-执行shell命令】及钉钉开放平台文档)。

配置完成后,返回主界面,进入创建的工程,选择左面的Build with Parameters进行参数化构建

参数化构建

构建完成后的邮件通知:

参数化构建

构建完成后的钉钉通知:

参数化构建

Jenkins内建环境变量

以下是Jenkins的一些内部环境变量,在你的Jenkins项目中可以找到例如:

`

http://192.168.10.123:8888/env-vars.html

`


The following variables are available to shell scripts

BRANCH_NAME

For a multibranch project, this will be set to the name of the branch being built, for example in case you wish to deploy to production from master but not from feature branches; if corresponding to some kind of change request, the name is generally arbitrary (refer to CHANGE_ID and CHANGE_TARGET).

CHANGE_ID

For a multibranch project corresponding to some kind of change request, this will be set to the change ID, such as a pull request number, if supported; else unset.

CHANGE_URL

For a multibranch project corresponding to some kind of change request, this will be set to the change URL, if supported; else unset.

CHANGE_TITLE

For a multibranch project corresponding to some kind of change request, this will be set to the title of the change, if supported; else unset.

CHANGE_AUTHOR

For a multibranch project corresponding to some kind of change request, this will be set to the username of the author of the proposed change, if supported; else unset.

CHANGE_AUTHOR_DISPLAY_NAME

For a multibranch project corresponding to some kind of change request, this will be set to the human name of the author, if supported; else unset.

CHANGE_AUTHOR_EMAIL

For a multibranch project corresponding to some kind of change request, this will be set to the email address of the author, if supported; else unset.

CHANGE_TARGET

For a multibranch project corresponding to some kind of change request, this will be set to the target or base branch to which the change could be merged, if supported; else unset.

BUILD_NUMBER

The current build number, such as "153"

BUILD_ID

The current build ID, identical to BUILD_NUMBER for builds created in 1.597+, but a YYYY-MM-DD_hh-mm-ss timestamp for older builds

BUILD_DISPLAY_NAME

The display name of the current build, which is something like "#153" by default.

JOB_NAME

Name of the project of this build, such as "foo" or "foo/bar".

JOB_BASE_NAME

Short Name of the project of this build stripping off folder paths, such as "foo" for "bar/foo".

BUILD_TAG

String of "jenkins-${JOB_NAME}-${BUILD_NUMBER}". All forward slashes ("/") in the JOB_NAME are replaced with dashes ("-"). Convenient to put into a resource file, a jar file, etc for easier identification.

EXECUTOR_NUMBER

The unique number that identifies the current executor (among executors of the same machine) that’s carrying out this build. This is the number you see in the "build executor status", except that the number starts from 0, not 1.

NODE_NAME

Name of the agent if the build is on an agent, or "master" if run on master

NODE_LABELS

Whitespace-separated list of labels that the node is assigned.

WORKSPACE

The absolute path of the directory assigned to the build as a workspace.

JENKINS_HOME

The absolute path of the directory assigned on the master node for Jenkins to store data.

JENKINS_URL

Full URL of Jenkins, like http://server:port/jenkins/ (note: only available if Jenkins URL set in system configuration)

BUILD_URL

Full URL of this build, like http://server:port/jenkins/job/foo/15/ (Jenkins URL must be set)

JOB_URL

Full URL of this job, like http://server:port/jenkins/job/foo/ (Jenkins URL must be set)

GIT_COMMIT

The commit hash being checked out.

GIT_PREVIOUS_COMMIT

The hash of the commit last built on this branch, if any.

GIT_PREVIOUS_SUCCESSFUL_COMMIT

The hash of the commit last successfully built on this branch, if any.

GIT_BRANCH

The remote branch name, if any.

GIT_LOCAL_BRANCH

The local branch name being checked out, if applicable.

GIT_URL

The remote URL. If there are multiple, will be GIT_URL_1, GIT_URL_2, etc.

GIT_COMMITTER_NAME

The configured Git committer name, if any.

GIT_AUTHOR_NAME

The configured Git author name, if any.

GIT_COMMITTER_EMAIL

The configured Git committer email, if any.

GIT_AUTHOR_EMAIL

The configured Git author email, if any.

SVN_REVISION

Subversion revision number that's currently checked out to the workspace, such as "12345"

SVN_URL

Subversion URL that's currently checked out to the workspace.

其他配置

不使用模板:

例如邮件配置可以指定为以下形式,


邮件由Jenkins自动发出(请勿回复!)<br />

项目名称:$PROJECT_NAME <br />

构建编号:# $BUILD_NUMBER <br />

构建状态:$BUILD_STATUS <br/>

触发原因:$CAUSE <br/>

日志地址:$BUILD_URL console <br/>

构建地址:<a href="$BUILD_URL">$BUILD_URL</a> <br/>

Apk地址:"http://192.168.10.123:8080/edu-release-v1.0.0.apk"

开发人员:<br/>

<a href="mailto:zhangsandev@gmail.com">张三</a> $nbsp;$nbsp;

<a href="mailto:lisidev@gmail.com">李四</a>

使用模板:

Jenkins默认自带groovy-html.template可以在邮件配置中使用

${SCRIPT,template="groovy-html.template"}进行配置,如果需要自己指定样式及模板,可将模板放置在.jenkins/email-template文件夹下(没有该文件夹手动创建)邮件配置中引用该模板即可。

groovy-html.template模板内容如下(我替换了一些英文为汉字):


<STYLE>

  BODY, TABLE, TD, TH, P {

    font-family: Calibri, Verdana, Helvetica, sans serif;

    font-size: 12px;

    color: black;

  }

  .console {

    font-family: Courier New;

  }

  .filesChanged {

    width: 10%;

    padding-left: 10px;

  }

  .section {

    width: 100%;

    border: thin black dotted;

  }

  .td-title-main {

    color: white;

    font-size: 200%;

    padding-left: 5px;

    font-weight: bold;

  }

  .td-title {

    color: white;

    font-size: 120%;

    font-weight: bold;

    padding-left: 5px;

    text-transform: uppercase;

  }

  .td-title-tests {

    font-weight: bold;

    font-size: 120%;

  }

  .td-header-maven-module {

    font-weight: bold;

    font-size: 120%;   

  }

  .td-maven-artifact {

    padding-left: 5px;

  }

  .tr-title {

    background-color: <%= (build.result == null || build.result.toString() == 'SUCCESS') ? '#27AE60' : build.result.toString() == 'FAILURE' ? '#E74C3C' : '#f4e242' %>;

  }

  .test {

    padding-left: 20px;

  }

  .test-fixed {

    color: #27AE60;

  }

  .test-failed {

    color: #E74C3C;

  }

</STYLE>

<BODY>

  <!-- BUILD RESULT -->

  <table class="section">

    <tr class="tr-title">

      <td class="td-title-main" colspan=2>

        BUILD ${build.result ?: 'COMPLETED'}

      </td>

    </tr>

    <tr>

      <td>构建路径:</td>

      <td><A href="${rooturl}${build.url}">${rooturl}${build.url}</A></td>

    </tr>

    <tr>

      <td>项目名称:</td>

      <td>${project.name}</td>

    </tr>

    <tr>

      <td>构建日期:</td>

      <td>${it.timestampString}</td>

    </tr>

    <tr>

      <td>构建用时:</td>

      <td>${build.durationString}</td>

    </tr>

    <tr>

      <td>触发原因:</td>

      <td><% build.causes.each() { cause -> %> ${cause.shortDescription} <%  } %></td>

    </tr>

    <tr>

    <td>开发人员</td>

    <td>

        <a href="mailto:wangxiaoliang@danyuantech.com">王晓亮</a> &nbsp;&nbsp:

            <a href="mailto:gaopengfei@danyuantech.com">高鹏飞</a>

    </td>

    </tr>

    <tr>

        <td>Apk下载:</td>

        <td><a href="http://192.168.10.123:8080/download/android/edu">下载</a></td>

    </tr>

  </table>

  <br/>

  <!-- CHANGE SET -->

  <%

  def changeSets = build.changeSets

  if(changeSets != null) {

    def hadChanges = false %>

  <table class="section">

    <tr class="tr-title">

      <td class="td-title" colspan="2">变更记录</td>

    </tr>

    <% changeSets.each() {

      cs_list -> cs_list.each() {

        cs -> hadChanges = true %>

    <tr>

      <td>

        修订版本

        <%= cs.metaClass.hasProperty('commitId') ? cs.commitId : cs.metaClass.hasProperty('revision') ? cs.revision : cs.metaClass.hasProperty('changeNumber') ? cs.changeNumber : "" %>

        by <B><%= cs.author %></B>

      </td>

      <td>${cs.msgAnnotated}</td>

    </tr>

        <% cs.affectedFiles.each() {

          p -> %>

    <tr>

      <td class="filesChanged">${p.editType.name}</td>

      <td>${p.path}</td>

    </tr>

        <% }

      }

    }

    if ( !hadChanges ) { %>

    <tr>

      <td colspan="2">No Changes</td>

    </tr>

    <% } %>

  </table>

  <br/>

  <% } %>

<!-- ARTIFACTS -->

  <%

  def artifacts = build.artifacts

  if ( artifacts != null && artifacts.size() > 0 ) { %>

  <table class="section">

    <tr class="tr-title">

      <td class="td-title">BUILD ARTIFACTS</td>

    </tr>

    <% artifacts.each() {

      f -> %>

      <tr>

        <td>

          <a href="${rooturl}${build.url}artifact/${f}">${f}</a>

      </td>

    </tr>

    <% } %>

  </table>

  <br/>

  <% } %>

<!-- MAVEN ARTIFACTS -->

  <%

  try {

    def mbuilds = build.moduleBuilds

    if ( mbuilds != null ) { %>

  <table class="section">

    <tr class="tr-title">

      <td class="td-title">BUILD ARTIFACTS</td>

    </tr>

      <%

      try {

        mbuilds.each() {

          m -> %>

    <tr>

      <td class="td-header-maven-module">${m.key.displayName}</td>

    </tr>

          <%

          m.value.each() {

            mvnbld -> def artifactz = mvnbld.artifacts

            if ( artifactz != null && artifactz.size() > 0) { %>

    <tr>

      <td class="td-maven-artifact">

              <% artifactz.each() {

                f -> %>

        <a href="${rooturl}${mvnbld.url}artifact/${f}">${f}</a><br/>

              <% } %>

      </td>

    </tr>

            <% }

          }

        }

      } catch(e) {

        // we don't do anything

      } %>

  </table>

  <br/>

    <% }

  } catch(e) {

    // we don't do anything

  } %>

<!-- JUnit TEMPLATE -->

  <%

  def junitResultList = it.JUnitTestResult

  try {

    def cucumberTestResultAction = it.getAction("org.jenkinsci.plugins.cucumber.jsontestsupport.CucumberTestResultAction")

    junitResultList.add( cucumberTestResultAction.getResult() )

  } catch(e) {

    //cucumberTestResultAction not exist in this build

  }

  if ( junitResultList.size() > 0 ) { %>

  <table class="section">

    <tr class="tr-title">

      <td class="td-title" colspan="5">${junitResultList.first().displayName}</td>

    </tr>

    <tr>

        <td class="td-title-tests">Name</td>

        <td class="td-title-tests">Failed</td>

        <td class="td-title-tests">Passed</td>

        <td class="td-title-tests">Skipped</td>

        <td class="td-title-tests">Total</td>

      </tr>

    <% junitResultList.each {

      junitResult -> junitResult.getChildren().each {

        packageResult -> %>

    <tr>

      <td>${packageResult.getName()}</td>

      <td>${packageResult.getFailCount()}</td>

      <td>${packageResult.getPassCount()}</td>

      <td>${packageResult.getSkipCount()}</td>

      <td>${packageResult.getPassCount() + packageResult.getFailCount() + packageResult.getSkipCount()}</td>

    </tr>

    <% packageResult.getPassedTests().findAll({it.getStatus().toString() == "FIXED";}).each{

        test -> %>

            <tr>

              <td class="test test-fixed" colspan="5">

                ${test.getFullName()} ${test.getStatus()}

              </td>

            </tr>

        <% } %>

        <% packageResult.getFailedTests().sort({a,b -> a.getAge() <=> b.getAge()}).each{

          failed_test -> %>

    <tr>

      <td class="test test-failed" colspan="5">

        ${failed_test.getFullName()} (Age: ${failed_test.getAge()})

      </td>

    </tr>

        <% }

      }

    } %>

  </table>

  <br/>

  <% } %>

<!-- CONSOLE OUTPUT -->

  <%

  if ( build.result == hudson.model.Result.FAILURE ) { %>

  <table class="section" cellpadding="0" cellspacing="0">

    <tr class="tr-title">

      <td class="td-title">控制台输出</td>

    </tr>

    <% build.getLog(100).each() {

      line -> %>

  <tr>

      <td class="console">${org.apache.commons.lang.StringEscapeUtils.escapeHtml(line)}</td>

    </tr>

    <% } %>

  </table>

  <br/>

  <% } %>

</BODY>


参考文章:

上一篇下一篇

猜你喜欢

热点阅读