在使用Jenkins的shell command
来执行python脚本时,总是会等脚本执行完毕,最后一次性才把脚本中的print
语句给打印出来;
在print
语句后加上sys.stdout.flush()
就可以达到实时输出的目的了。
Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能。
在使用Jenkins的shell command
来执行python脚本时,总是会等脚本执行完毕,最后一次性才把脚本中的print
语句给打印出来;
在print
语句后加上sys.stdout.flush()
就可以达到实时输出的目的了。
Hudson/Jenkins
中配置svn
的URL
时,有时会出现如下警告信息:
1 |
“WARNING: clock of the subversion server appears to be out of sync. This can result in inconsistent check out behavior” |
基本选择每次重新checkout
最新的版本,也会出现该警告信息。
并且经常无法获取到最新的代码,要等几分钟之后才能拉取到最新的代码。
这个在编译的时候有时候会有这个警告,主要是svn
等服务器的时钟和本机的时钟有偏差,比如svn
的时钟比较快,jenkins
的时钟慢,当你提交svn
的时候,紧接着进行jenkins
构建,这个时候jenkins
的svn update
由于时钟原因就更新不到刚才的数据,但过了几分钟之后再进行更新的时候,超过两台机器的时间差,在次进行jenkins
构建,就可以update
到数据。
继续阅读Hudson/Jenkins -- 消除svn警告: "clock of the subversion server appears to be out of sync"
默认情况下,Jenkins在构建的时候会输出脚本中命令的具体内容,原因是在执行脚本的时候,使用的是-e
参数执行的。
但是当我们的脚本中存在用户名,密码等关键信息的时候,这些内容会被输出到编译日志中,这样就容易导致安全问题。
解决办法是在脚本的最前面增加#!/bin/bash
,来覆盖脚本执行的默认参数,如下图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# 要求Java 1.8 $ yum install java $ yum install subversion $ yum install tomcat $ /bin/systemctl restart tomcat.service $ wget https://get.jenkins.io/war/2.254/jenkins.war $ cp jenkins.war /usr/share/tomcat/webapps/ $ /bin/systemctl restart tomcat.service # 服务器访问地址 # http://158.220.155.188:8080/jenkins/ # apache $ yum install httpd $ /bin/systemctl restart httpd.service # apache 默认工作目录 /var/www/html/ |
为了安全考虑,首先需要解锁Jenkins
最近在搭建Jenkins
环境,实现Android
自动化编译的过程中,由于内网服务器不能访问外网,因此只能配置Robolectric
访问内网的服务器。
根据官方文档 Configuring Robolectric,发现如果要更改Robolectric
默认的下载服务器链接地址,需要在项目的每个lib
库中都配置如下参数,才能实现:
1 2 3 4 5 6 7 8 |
android { testOptions { unitTests.all { systemProperty 'robolectric.dependency.repo.url', 'https://local-mirror/repo' systemProperty 'robolectric.dependency.repo.id', 'local' } } } |
但是这样配置有一个问题,就是没办法动态调整链接地址。
而我们使用
1 |
$ bash gradlew clean build -Drobolectric.dependency.repo.url=http://127.0.0.1/jcenter |
手工指定的参数,并没有在编译的时候生效。
参数丢失的原因是因为测试用例在新的JVM
中执行,传入的参数不会自动带给新创建的JVM
。
这时需要在Gradle
脚本中将读到的值重新设到系统属性里面,才可以被Java
程序读到。
1 2 3 4 5 6 7 8 9 10 11 |
android { testOptions { unitTests.all { //命令行下 单元测试可能卡住的问题 jvmArgs '-noverify' //robolectric外部指定下载资源链接的参数,使用 -D 参数指定 bash gradlew clean build -Drobolectric.dependency.repo.url=http://127.0.0.1/jcenter systemProperty 'robolectric.dependency.repo.url', System.getProperty("robolectric.dependency.repo.url") systemProperty 'robolectric.dependency.repo.id', System.getProperty("robolectric.dependency.repo.id") } } } |
每个项目的build.gradle
中都增加上面的配置之后,就可以保证在外部编译的时候动态指定参数了。
在 Android Studio Chipmunk | 2021.2.1 Patch 1 以及以上的版本,可能会发生如下报错:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
URI is not absolute java.lang.IllegalArgumentException: URI is not absolute at java.base/java.net.URL.fromURI(URL.java:692) at java.base/java.net.URI.toURL(URI.java:1116) at org.robolectric.internal.dependency.MavenArtifactFetcher.getRemoteUrl(MavenArtifactFetcher.java:138) at org.robolectric.internal.dependency.MavenArtifactFetcher.fetchToStagingRepository(MavenArtifactFetcher.java:145) at org.robolectric.internal.dependency.MavenArtifactFetcher.fetchArtifact(MavenArtifactFetcher.java:65) at org.robolectric.internal.dependency.MavenDependencyResolver.lambda$getLocalArtifactUrls$0(MavenDependencyResolver.java:80) at org.robolectric.internal.dependency.MavenDependencyResolver.whileLocked(MavenDependencyResolver.java:100) at org.robolectric.internal.dependency.MavenDependencyResolver.getLocalArtifactUrls(MavenDependencyResolver.java:75) at org.robolectric.internal.dependency.MavenDependencyResolver.getLocalArtifactUrls(MavenDependencyResolver.java:65) at org.robolectric.internal.dependency.MavenDependencyResolver.getLocalArtifactUrl(MavenDependencyResolver.java:116) at org.robolectric.plugins.LegacyDependencyResolver.getLocalArtifactUrl(LegacyDependencyResolver.java:89) at org.robolectric.plugins.DefaultSdkProvider$DefaultSdk.getJarPath(DefaultSdkProvider.java:146) at org.robolectric.internal.AndroidSandbox$SdkSandboxClassLoader.<init>(AndroidSandbox.java:102) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490) at org.robolectric.util.inject.Injector.inject(Injector.java:250) at org.robolectric.util.inject.Injector.lambda$memoized$1(Injector.java:232) at org.robolectric.util.inject.Injector$MemoizingProvider.get(Injector.java:498) at org.robolectric.util.inject.Injector.getInstanceInternal(Injector.java:224) at org.robolectric.util.inject.Injector.resolveDependencies(Injector.java:296) at org.robolectric.util.inject.Injector.inject(Injector.java:248) at org.robolectric.util.inject.Injector.lambda$memoized$1(Injector.java:232) at org.robolectric.util.inject.Injector$MemoizingProvider.get(Injector.java:498) at org.robolectric.util.inject.Injector.getInstanceInternal(Injector.java:224) at org.robolectric.util.inject.Injector.getInstance(Injector.java:208) at org.robolectric.util.inject.Injector.access$700(Injector.java:96) at org.robolectric.util.inject.Injector$ScopeBuilderProvider.create(Injector.java:564) at org.robolectric.util.inject.Injector$ScopeBuilderProvider.lambda$get$0(Injector.java:547) at com.sun.proxy.$Proxy19.build(Unknown Source) at org.robolectric.internal.SandboxManager.getAndroidSandbox(SandboxManager.java:57) at org.robolectric.RobolectricTestRunner.getSandbox(RobolectricTestRunner.java:285) at org.robolectric.RobolectricTestRunner.getSandbox(RobolectricTestRunner.java:68) at org.robolectric.internal.SandboxTestRunner$2.evaluate(SandboxTestRunner.java:236) at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63) at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329) at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293) at org.robolectric.internal.SandboxTestRunner$1.evaluate(SandboxTestRunner.java:93) at org.junit.runners.ParentRunner.run(ParentRunner.java:413) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38) at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62) at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33) at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94) at com.sun.proxy.$Proxy2.processTestClass(Unknown Source) at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176) at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129) at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100) at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60) at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56) at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133) at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71) at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74) |
该报错的原因是 Android Studio 没有正确读取我们配置的 robolectric.dependency.repo.url 环境变量,导致无法成功下载,当然也可能是由于高版本不支持 HTTP ,链接地址必须是 HTTPS 导致此问题,这个没有深究。
解决方法是,在命令行执行
1 |
$ bash gradlew clean build -Drobolectric.dependency.repo.url=http://127.0.0.1/jcenter |
确保在当前用户目录下的 .m2 目录下,成功完成 robolectric 需要的全部文件的下载。这样,执行测试用例的时候,就可以从本地获取依赖文件,而不需要再去服务器上获取。
Jenkins
官网下载,如果官网下载存在问题,也可以在本站下载Jenkins。
Gradle
目前Android Studio
支持的是Gradle 2.8
版本,因此下载2.8版本的即可。Gradle 2.8,也可以本站下载。
官方主页Apache Tomcat。Windows下面建议下载32-bit/64-bit Windows Service Installer
版本。
Jenkins
将下载的jenkins.war
包直接放到tomcat
下的webapps
目录,启动tomcat
,在浏览器输入:http://127.0.0.1:8080/jenkins
Git plugin
,Gradle plugin
,Android Lint Plugin
插件点击Jenkins
首页的"Manage Jenkins
"链接,如下图:
进入设置,点击"Manage Plugins
",添加Git plugin
,Gradle plugin
,Android Lint Plugin
在打开的页面中,搜索并且安装插件
返回首页,点击" Manage Plugins
",然后进入页面中选择Configure System
,配置JDK
,Gradle
,Git
的选项。
设置JDK
首页点击"创建一个新任务
",如下图:
在接下来的页面中,输入工程的名字,并且选择"Freestyle project
"
设置Git
中源代码的路径,如果是使用SSH
证书认证的登陆,则在Credentials
中进行配置,如下图:
接下来配置触发构建的条件,目前我们设置为每天晚上3点,注意里面输入的是H 3 * * *
,每个字符之间都有一个英文的空格。
接下来,配置Gralde
的编译,在构建
项目中选择"Invoke Gradle Script
",如下图:
在选项的Tasks
栏目中输入clean build --stacktrace --debug
,如下图:
接下来,配置构建后操作
,一般增加Publish Android Lint results
,Archive the artifacts
这两项即可,具体的配置参考下图:
构建完成以后检查一下,如果在:
1 |
当前用户目录\.jenkins\jobs\Android\workspace\app\build\outputs\apk\ |
下面成功生成了APK
文件,则说明配置是成功的。