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


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

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即可。

  還有幾個插件要安裝一下,進入到系統管理->管理插件->可選插件,安裝一下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 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------


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM