本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
转载自夜明的孤行灯
本文链接地址: https://www.huangyunkun.com/2014/01/17/gradle_svn_sae/
现在像SAE这类的应用引擎已经比较多了,百度和腾讯都提供了相似的平台。
我很早的时候就开始用SAE,当时还为了迁就SAE学习了PHP(当时只支持PHP和另外一个什么语言)。后来SAE支持Java了,版本是6,容器是Jetty 7.4,而常用的框架也基本能跑。
代码的部署使用svn,稍微有点麻烦。最近在做一个Java Web的练习,代码放在github上,每次上传到SAE很烦。项目使用Gradle管理,所以琢磨着怎么把自动发布到SAE这个事情交给Gradle来做。
流程分析
先来看一下SAE这边。
建立默认版本,版本号为1。然后通过代码管理页面上传一个war包。
进入svn仓库看一看
其实就是把对应的war包放在版本号目录的根目录就可以了。
而调用
gradle war
就可以生成war包,而文件地址通过以下方法获得
war.archivePath
这样就只需要checkout原有代码,然后将新的war拷贝过去,然后commit就行了。
直接调用SVN命令
Gradle有一种任务类型是Exec,可以直接在命令行调用命令。
比如我们的checkout操作就可以这样
task checkoutRepo(type:Exec) {
workingDir'build/'
commandLine'svn','checkout',repo_path,"svn"
}
这样代码仓库就被签出到build/svn目录下了。
在使用copy指令
copy{
from war.archivePath
into"build/svn/"+numberVersion
}
当然,gradle生成的war包的名字组成是
${baseName}-${appendix}-${version}-${classifier}.${extension}
如果要重命名的话,可以使用rename,比如利用重命名去掉version信息
copy {
from war.archivePath
into svnPath +"/" + numberVersion +"/"
rename { String fileName ->
fileName.replace("-" +version,'')
}
}
然后再次提交
workingDir'build/svn'
commandLine'svn','commit','-m',"Built By Gradle in"+newDate()
为了方便调试还可以导出命令运行的输出,提交部分的任务如下
task commitWarToSae(type:Exec) {
workingDir'build/svn'
commandLine'svn','commit','-m',"Built By Gradle in"+newDate()
standardOutput=new ByteArrayOutputStream()
ext.output= {
return standardOutput.toString()
}
}
这样勉强可以完成任务,但是问题是我的运行环境不一定包含了svn。我平时用的也比较少,不想安装它。这样只有使用其他方法了。
在Gradle中调用SVNKit
SVNKit 是一个纯 Java 的 SVN 客户端库,使用 SVNKit 无需安装任何 SVN 的客户端,支持各种操作系统。使用它一方面不需要考虑安装问题,另一方面SVNKit在maven仓库中有,不需要手动管理,最新版本为1.7.8。
首先在buildscript中配置SVNKit依赖
buildscript {
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
classpath(
'org.tmatesoft.svnkit:svnkit:1.7.8',
'org.tmatesoft.svnkit:svnkit-cli:1.7.8'
)
}
}
然后引用cli中的SVN,这样就可以直接调用程序的方法。
import org.tmatesoft.svn.cli.SVN;
调用时使用
SVN.main(String[] args)
这是可以直接将上文的调用svn的地方全部改为调用SVN.main,但是这之前还有一个问题。
SVN.main实质上只有一句话,调用了svn目录下的SVN类,而这个类继承了AbstractSVNLauncher。而AbstractSVNLauncher的代码中包含了错误处理
publicvoidfailure() {
setCompleted();
try {
System.exit(1);
}catch (SecurityException se) {
}
}
publicvoidsuccess() {
setCompleted();
try {
System.exit(0);
}catch (SecurityException se) {
}
}
对于System.exit()的调用明显是我们不希望的,即使SVNKit的运行出现了错误,我们依然期望其他任务继续运行。
通过Java的SecurityManager来达到目的
def _disableSystemExitCall = {
System.setSecurityManager(
new SecurityManager() {
@Override
publicvoidcheckPermission(java.security.Permission perm) {}
@Override
publicvoidcheckExit(int status) {thrownew SecurityException(); }
}
);
};
def _enableSystemExitCall = { System.setSecurityManager(null); };
def doSvn = { String... aSvnArgs ->
_disableSystemExitCall();
try {
SVN.main(aSvnArgs as String[]);
}finally {
_enableSystemExitCall();
}
};
配置完成了,先来测试一下帮助信息的显示
task('showSvnHelp') << {
doSvn("help")
}
运行
gradle showSvnHelp
可以看到效果。
剩下的事情就简单了,把所有事情放到一个新的任务中去,命名为uploadWarToSae
task('uploadWarToSae') << {
ext {
repo = YOUR_REPO_PATH
username = YOUR_USER_NAME
password = YOUR_PASSWORD
numberVersion = YOUR_NUMBER_VERSION
svnPath ='build/svn'
}
delete(svnPath)
doSvn("checkout", repo, svnPath,"--username", username,"--password", password)
copy {
from war.archivePath
into svnPath +"/" + numberVersion +"/"
rename { String fileName ->
fileName.replace("-" +version,'')
}
}
doSvn("commit","-m","Built By Gradle in" +new Date(),"--username", username,"--password", password, svnPath)
}
试着运行看看,你可以会遇到一下问题
- Server certificate verification failed
- Fingerprint: * (R)eject, accept (t)emporarily or accept (p)ermanently?
- failed: SSL error: certificate verify failed
从svn的帮助中可以找到这几个指令:—no-auth-cache —non-interactive —trust-server-cert
将它们加入doSvn中(其实也可以在调用的时候添加)
def doSvn= {String... aSvnArgs->
_disableSystemExitCall();
List<string> cmds= aSvnArgs as LinkedList<string>;
cmds.add("--no-auth-cache");
cmds.add("--non-interactive");
cmds.add("--trust-server-cert")
try {
SVN.main(cmds asString[]);
} finally {
_enableSystemExitCall();
}
};
这样就没有问题了。
如果你觉得用户名和密码直接写入build.gradle不安全,你也可以考虑将私密信息写入gradle.properties中去。
集成Travis
Travis是一个和Github集成的非常好的CI。使用它你需要在项目中配置.travis.yml文件。
我希望让travis自动将每次的更改打包后上传到SAE中,当然直接调用
gradle uploadWarToSae
就行了。但是私密信息写在哪里?
Travis提供了利用RSA密匙加密的方法。具体细节参考文末链接。
travis encrypt-a env.global USERNAME=username PASSWORD=password
这样就可以在命令行中调用了,比如
gradle uploadWarToSae -Pusername=$USERNAME -Ppassword=$PASSWORD
然后改造uploadWarToSae方法,让其可以接受参数。
task('uploadWarToSae') << {
if (project.hasProperty('username') &&project.hasProperty("password")) {
ext {
repo ='https://svn.sinaapp.com/blackjack/'
numberVersion =1
svnPath ='build/svn'
}
delete(svnPath)
doSvn("checkout", repo, svnPath,"--username", username,"--password", password)
copy {
from war.archivePath
into svnPath +"/" + numberVersion +"/"
rename { String fileName ->
fileName.replace("-" + version,'')
}
}
doSvn("commit","-m","Built in" +new Date(),"--username", username,"--password", password, svnPath)
}else {
thrownew InvalidUserDataException("没有提供sae的安全邮箱和密码,部署失败")
}
}
运行效果如下:
结语
如果使用的是BAE的话,官方提供了maven的部署插件的…而SAE不知道有没有提供,反正从官方文档中没有看到。
附上几个参考地址:
Preventing System.exit() from API
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
转载自夜明的孤行灯
本文链接地址: https://www.huangyunkun.com/2014/01/17/gradle_svn_sae/