jenkins+svn+android studio自動化構建(持續集成)

  先到Jenkins官網的Meet Jekins中看一下Installation部分,原文以下html

You have several options for downloading and installing Jenkins: *Use one of the platform-specific package/installer links on the Jenkins site to install Jenkins on your system. *You can download jenkins.war directly and launch it by executing java -jar jenkins.war. This is basically the same set up as the test drive, except that the output will go to console, not to a window. On Windows, you can even choose to install Jenkins as a service afterwards. *If you have a servlet container that supports Servlet 2.4/JSP 2.0 or later, such as Tomcat 5, you can deploy jenkins.war as you would any WAR file. See this document for more about container-specific installation instruction.

  大概的看一下,意思就是說有好幾種方法下載和安裝Jenkins,針對windows操做系統能夠選擇的方法是,下載Jenkins提供的exe直接安裝,以服務的方式運行,還有一種是下載Jenkins提供的war,war文件須要經過tomcat安裝,還有許多的配置項須要設置,簡單起見,本文采用exe的方式進行安裝,下載完成以後解壓出來,運行setup.exe,安裝完成以後進入控制面板->管理工具->服務就能夠看到jenkins。經過http://localhost:8080/就能夠進入到jenkins的web頁面了,若是須要配置jenkins的工做目錄,先中止jenkins服務,而後再增長環境變量JENKINS_HOME便可。java

  還有幾個插件要安裝一下,進入到系統管理->管理插件->可選插件,安裝一下Email Extension Plugin(用於郵件通知)和Gradle plugin(用於gradle編譯)這兩個插件。安裝完成以後,再配置一些信息
android

  •   進入到系統管理->系統設置,修改系統設置

    Jenkins Location標籤中git

      系統管理員郵件地址:sender@163.comgithub

    郵件通知標籤中web

      SMTP服務器:smtp.163.comwindows

      用戶名:sendertomcat

      密碼: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 only
task buildAll(){ println 'start build' } svnCommitVersionFile.dependsOn build generateZip.dependsOn svnCommitVersionFile buildAll.dependsOn generateZip tasks.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 cleanNative def 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裏面,編譯一下,結果生成了,看一下咱們收到的郵件

  編譯日誌,編譯結果,上一次構建到此次構建的svn提交記錄均可以看到了,做爲一個簡單的持續集成環境仍是能夠的。


2016.01.16

------End------

相關文章
相關標籤/搜索