jenkins+svn+android studio自动化构建(持续集成)

来源:互联网 发布:javascript生成随机数 编辑:程序博客网 时间:2024/04/30 12:52

 大概的看一下,意思就是说有好几种方法下载和安装Jenkins,针对windows操作系统可以选择的方法是,下载Jenkins提供的exe直接安装,以服务的方式运行,还有一种是下载Jenkins提供的war,war文件需要通过tomcat安装,还有许多的配置项需要设置,简单起见,本文采用exe的方式进行安装,下载完成之后解压出来,运行setup.exe,安装完成之后进入控制面板->管理工具->服务就可以看到jenkins。通过http://localhost:8080/就可以进入到jenkins的web页面了,如果需要配置jenkins的工作目录,先停止jenkins服务,然后再增加环境变量JENKINS_HOME即可。

  还有几个插件要安装一下,进入到系统管理->管理插件->可选插件,安装一下Email Extension Plugin(用于邮件通知)和Gradle plugin(用于gradle编译)这两个插件。安装完成之后,再配置一些信息

  •   进入到系统管理->系统设置,修改系统设置

    Jenkins Location标签中

      系统管理员邮件地址:sender@163.com

    邮件通知标签中

      SMTP服务器:smtp.163.com

      用户名:sender

      密码:xxxxxx

      SMTP端口:选中SSL填465,没选中填25

      Reply-To Address:sender@163.com

      字符集:UTF-8

  •   项目设置

    高级项目选项标签中
      使用自定义的工作空间:可以配置自定义的工作空间

    源码管理标签中Subversion  

      Repository URL: svn地址,注意这里都要填写小写的字母,否则如果真是svn路径中有大写字母的话,会导致svn版本号获取不到以及变更集获取不到
      Local modle directory(optional):.
      Repository depth:infinity
      Check out Strategy:use 'svn update' as much as possible  

    构建标签中
      Gradle Version:选中最新版本吧,我选的是2.9
      Tasks:clean buildAll,这个是build.gradle中的任务,后面会把测试工程的build.gradle放出来

    构建后操作步骤中加入Archive the artifacts,Email Notificatioin,Editable Email Notification    
       Archive the artifacts标签中
        用于存档的文件:app/build/release/*.zip
        Email Notificatioin标签中
        Recipients:收件人的名字,这里是配置发送编译错误以及编译恢复邮件的收件人
      Editable Email Notification标签中要增加触发器,成功后发送给Rccipient List
      Project Recipient List:receiver1@163.com,receiver2@163.com,...,receivern@163.com
      Project Reply-To List:sender@163.com
      Content Type:HTML(text/html)
      Default Subject:构建通知:$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!
      Default Content:
        (本邮件是程序自动下发的,请勿回复!)<br/>
        项目名称:$PROJECT_NAME<br/>
        构建编号:$BUILD_NUMBER<br/>
        svn版本号:${SVN_REVISION}<br/>
        构建状态:$BUILD_STATUS<br/>
        触发原因:${CAUSE}<br/>
        构建日志地址:<a href="${BUILD_URL}console">${BUILD_URL}console</a><br/>
        构建地址:<a href="$BUILD_URL">$BUILD_URL</a><br/>
        变更集:${JELLY_SCRIPT,template="html"}<br/>
  这里是分割线,服务器的配置都ok了,Android Studio的工程当然也是要做一些配合才可以完成的,下面把Android Studio工程中app的build.gradle贴出来

复制代码
import org.tmatesoft.svn.core.wc.*import org.tmatesoft.svn.core.wc2.*import org.tmatesoft.svn.core.*apply plugin: 'com.android.application'android {    compileSdkVersion 22    buildToolsVersion "22.0.1"    defaultConfig {        // 包名        applicationId "com.zhb.studiotest"        minSdkVersion 19        targetSdkVersion 22        versionCode 1        versionName "1.0.0.1"        manifestPlaceholders = [ CHANNEL_NAME:"Unspecified",APPLICATION_LABLE:"StudioTest"]    }    sourceSets.main.jni.srcDirs = []    sourceSets.main.jniLibs.srcDir 'src/main/libs'    def versionPropsFile = file('version.properties')    if (versionPropsFile.canRead()) {        def Properties versionProps = new Properties()        versionProps.load(new FileInputStream(versionPropsFile))        def prename = versionProps['VERSION_NAME_MAJOR']        def name = versionProps['VERSION_NAME_BUILD'].toInteger()        def runTasks = gradle.startParameter.taskNames        if ('buildAll' in runTasks) {            name++        }        versionProps['VERSION_NAME_BUILD']=name.toString()        versionProps.store(versionPropsFile.newWriter(), null)        defaultConfig {            versionName prename + name        }    }    signingConfigs {        releaseConfig {            // 写死签名密码            keyAlias 'xxx'            keyPassword 'xxxx'            storeFile file("keystore.jks")            storePassword 'xxxx'            // 要求输入签名密码//            storeFile file("keystore.jks")//            keyAlias System.console().readLine("\nkeyAlias: ")//            storePassword System.console().readLine("\nKeystore password: ")//            keyPassword System.console().readLine("\nKey password: ")        }    }    /*productFlavors {        xiaomi {            applicationId = "com.zhb.xiaomi"            manifestPlaceholders = [UMENG_CHANNEL_VALUE: name,APPLICATION_LABLE:name]        }        baidu {            applicationId = "com.zhb.baidu"            manifestPlaceholders = [UMENG_CHANNEL_VALUE: name,APPLICATION_LABLE:name]        }        wandoujia {            applicationId = "com.zhb.wandoujia"            manifestPlaceholders = [UMENG_CHANNEL_VALUE: name,APPLICATION_LABLE:name]        }    }*/    productFlavors {        wandoujia {}        baidu {}        //c360 {}        //uc {}        productFlavors.all { flavor ->            applicationId = "com.zhb."+name            flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name,APPLICATION_LABLE:name]        }         }    buildTypes {        debug {            buildConfigField "boolean", "LOG_DEBUG", "true"            versionNameSuffix "-debug"            // 混淆开关            minifyEnabled false            // 在Android中,每个应用程序中储存的数据文件都会被多个进程访问:            // 安装程序会读取应用程序的manifest文件来处理与之相关的权限问题;            // Home应用程序会读取资源文件来获取应用程序的名和图标;            // 系统服务会因为很多种原因读取资源(例如,显示应用程序的Notification);            // 此外,就是应用程序自身用到资源文件。            // 当资源文件通过内存映射对齐到4字节边界时,访问资源文件的代码才是有效率的。            zipAlignEnabled false            // 删除没用的资源文件            shrinkResources false        }        release {            buildConfigField "boolean", "LOG_DEBUG", "false"            minifyEnabled true            zipAlignEnabled true            shrinkResources true            // 混淆文件            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'            // 签名            signingConfig signingConfigs.releaseConfig            applicationVariants.all { variant ->                // 修改APK名称                variant.outputs.each { output ->                    def file = output.outputFile                    def fileName = file.name                    fileName = fileName.replace(".apk", "-V${defaultConfig.versionName}.apk")                    fileName = fileName.replace("app", "StudioTest")                    fileName = fileName.replace("debug-unaligned", "debug")                    output.outputFile = new File(file.parent, fileName)                }                // 修改values.xml                variant.mergeResources.doLast(){                    File valuesFile = file("${buildDir}/intermediates/res/merged/${variant.dirName}/values/values.xml")                    String content = valuesFile.getText('UTF-8')                    content = content.replaceAll("CHANNEL_NAME","${variant.productFlavors[0].name}")                    valuesFile.write(content,'UTF-8')                }            }        }    }}def getSvnRevision(){    ISVNOptions options = SVNWCUtil.createDefaultOptions(true);    SVNClientManager clientManager = SVNClientManager.newInstance(options);    SVNStatusClient statusClient = clientManager.getStatusClient();    SVNStatus status = statusClient.doStatus(project.rootDir, false);    SVNRevision revision = status.getRevision();    return revision.getNumber();}task svnCommitVersionFile(){    description = "Commits a single file to an SVN repository"    doLast{        if (!project.hasProperty("commitMsg")){            ext.commitMsg = "//change version"        }        SvnOperationFactory svnOperationFactory = new SvnOperationFactory()        def authentication = SVNWCUtil.createDefaultAuthenticationManager("haibo.zhou", "hbzhou0622")        svnOperationFactory.setAuthenticationManager(authentication)        try {            SvnCommit commit = svnOperationFactory.createCommit()            commit.setSingleTarget(SvnTarget.fromFile(new File('app/version.properties')))            commit.setCommitMessage(commitMsg)            SVNCommitInfo commitInfo = commit.run()            println "Commit info: " + commitInfo            println "Commit message: " + commitMsg        } finally{            svnOperationFactory.dispose()        }    }}task generateZip(type: Zip){    def versionPropsFile = file('version.properties')    def Properties versionProps = new Properties()    versionProps.load(new FileInputStream(versionPropsFile))    def prename = versionProps['VERSION_NAME_MAJOR']    def name = versionProps['VERSION_NAME_BUILD'].toInteger()    def version = "V" + prename + name + "_(" + getSvnRevision() + ")"    from 'build/outputs'    archiveName "StudioTest_" + version + ".zip"    destinationDir file("build/release")    doLast(){        copy{            from ("build/release/"+archiveName)            into ("release")        }        if (!project.hasProperty("commitMsg")){            ext.commitMsg = "//upload compile result"        }        SvnOperationFactory svnOperationFactory = new SvnOperationFactory()        def authentication = SVNWCUtil.createDefaultAuthenticationManager("haibo.zhou", "hbzhou0622")        svnOperationFactory.setAuthenticationManager(authentication)        try {            SvnScheduleForAddition add = svnOperationFactory.createScheduleForAddition();            SvnTarget target = SvnTarget.fromFile(new  File("app/release/"+archiveName));            add.addTarget(target);            add.setAddParents(true);            add.setForce(true);            add.run();            SvnCommit commit = svnOperationFactory.createCommit()            commit.setSingleTarget(SvnTarget.fromFile(new File("app/release/"+archiveName)))            commit.setCommitMessage(commitMsg)            SVNCommitInfo commitInfo = commit.run()            println "Commit info: " + commitInfo            println "Commit message: " + commitMsg        } finally{            svnOperationFactory.dispose()        }    }}// build script for jenkins onlytask buildAll(){    println 'start build'}svnCommitVersionFile.dependsOn buildgenerateZip.dependsOn svnCommitVersionFilebuildAll.dependsOn generateZiptasks.withType(JavaCompile) {    compileTask -> compileTask.dependsOn ndkBuild}task ndkBuild(type: Exec) {    workingDir file('src/main/jni')    commandLine getNdkBuildCmd()}task cleanNative(type: Exec){    workingDir file('src/main/jni')    commandLine getNdkBuildCmd(), 'clean'}clean.dependsOn cleanNativedef getNdkDir() {    if (System.env.ANDROID_NDK_ROOT != null)        return System.env.ANDROID_NDK_ROOT    Properties properties = new Properties()    properties.load(project.rootProject.file('local.properties').newDataInputStream())    def ndkdir = properties.getProperty('ndk.dir', null)    if (ndkdir == null)        throw new GradleException("NDK location not found. Define location with ndk.dir in the local.properties file or with an ANDROID_NDK_ROOT environment variable.")    return ndkdir}def getNdkBuildCmd() {    def ndkbuild = getNdkDir() + "/ndk-build"    ndkbuild += ".cmd"    return ndkbuild}dependencies {    // 工程目录里面的libs文件夹下所有的jar包    compile fileTree(dir: 'libs', include: ['*.jar'])    // 网络仓库里面的工程    //compile 'com.github.chrisbanes.photoview:library:1.2.4'    // 本地的工程    compile project(':PhotoView-master')}
复制代码

  大概描述一下脚本,在控制台运行gradle buildAll,将会把build/output目录打成压缩生成到build/release目录,并自动修改版本号文件version.property提交到svn,同时将编译结果提交到svn。

  ok,在到我们的jenkins里面,编译一下,结果生成了,看一下我们收到的邮件

1
<span style="font-size: 14px;">  编译日志,编译结果,上一次构建到这次构建的svn提交记录都可以看到了,作为一个简单的持续集成环境还是可以的。<br><br><br></span>

2016.01.16

0 0
原创粉丝点击