使用Gradle自动发布Java Web到SAE

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

转载自夜明的孤行灯

本文链接地址: 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不知道有没有提供,反正从官方文档中没有看到。

附上几个参考地址:

AbstractSVNLauncher.java

Preventing System.exit() from API

disabling System.exit()

encryption-keys

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

转载自夜明的孤行灯

本文链接地址: https://www.huangyunkun.com/2014/01/17/gradle_svn_sae/

发表评论