美文网首页
Android Jenkins CI集成

Android Jenkins CI集成

作者: PengFly | 来源:发表于2018-11-13 15:44 被阅读114次

    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需要如下插件(根据个人及公司需要自行选择):

    • Email Extension Plugin(用于构建后邮件通知)

    • Git Parameter Plug-In(提供参数化构建)

    • Gradle Plugin(用户打包编译)

    • Locale plugin(用于汉化Jenkins)

    • Dingding[钉钉] Plugin(用于构建后使用webhook通知)

    新建任务及配置

    主界面

    主界面

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

    系统设置】

    进入系统设置,配置全局变量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>
    
    
    • 构建后配置-归档成品:该配置只适用于将成品生成到workspaces下,其他路径不生效,归档成品后会在工程页面展示成品信息

    • 构建后发布到蒲公英:文档


    参考文章:

    相关文章

      网友评论

          本文标题:Android Jenkins CI集成

          本文链接:https://www.haomeiwen.com/subject/ftdlfqtx.html