本篇不是长篇大论服务器的架设,只是谈与svn或者git相关的内容。
首先说下自己的思路,每个开发人员将代码提交到svn服务器的时候,触发post-commit钩子,使得代码自动更新到测试服务器,在测试服务器上查看代码上线后是否OK。如果没问题,则对与测试服务器环境完全一致的线上服务器的代码进行手动更新。手动更新的方式为通过web访问,web访问一个php脚本,php脚本去执行shell命令来更新。
如果我们每次都是对线上服务器做ssh连接,然后执行svn up的命令实在有点儿浪费时间。而且不是所有人都权限去访问线上服务器,如果所有人更新代码都要一个人去更新,更是麻烦。而且,笔者现在的实际开发环境中,线上服务器只对外开放了一个80端口,想连ssh,还得先连自己搭建的vpn。所以我们最好是能直接通过web端进行更新。
代码github地址 https://github.com/zhoumengkang/svn-hook-to-remote-server
下面说下实现的代码的详细解释:
首先是svn服务器上的钩子post-commit代码
#!/bin/sh
export LANG="zh_CN.UTF-8"
#更新到远程的服务器
#/home/wwwroot/xxx/svn.php 为svn.php在svn服务器上的存放地址
/usr/local/php/bin/php /home/wwwroot/xxx/svn.php
这里是在post-commit里面执行一个php脚本。下面看这个脚本(svn.php)的代码:
/**
* svn钩子post-commit里执行的文件
* 存放在svn服务器上
*/
$updateUrl = "http://test.mengkang.net/update.php";//远程测试服务器上的update.php的地址
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $updateUrl);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
在这个脚本里是使用的curl去模拟访问了测试服务器上的URL
http://test.mengkang.net/update.php
/**
* svn服务器上的钩子需要模拟访问的文件,必须是外网可以访问的
* 存放在远程服务器上
* @author zhoumengkang <i@zhoumengkang.com>
*/
error_reporting(E_ALL);
//"/home/wwwroot/test/" 为代码更新到的指定目录路径
putenv("LC_CTYPE=zh_CN.UTF-8");//有时候字符集会很坑爹,所以设置环境变量还是很有必要的
$handle = popen('svn up --username zmk --password 123456 /home/wwwroot/test/ 2>&1','r');
//echo "'$handle'; " . gettype($handle) . "\n";
$read = stream_get_contents($handle);
//TODO 如果在$read中可以匹配到“error/conflict”,就应该发送邮件到管理员的邮箱了!
echo $read;
pclose($handle);
稍微做点安全判断扩展版
<?php
header("Cache-Control:no-cache,must-revalidate");
//首先判断ip是否合法
if(!preg_match('/123\.123\.123\.[0-9]+/', $_SERVER['REMOTE_ADDR']){
//可以给管理员发送邮件
die('ip不合法');
}
?>
<form action="" method="post">
<input type="password" name="password" id="password" value="">
<input type="submit" value="提交" onclick="storePassword();">
<script type="text/javascript">
//自动填充密码
function setValue(){
var passwordInput = document.getElementById('password');
passwordInput.value = getcookie('svnupdate');
}
window.onload=setValue;
//存储密码
function storePassword(){
var passwordInputValue = document.getElementById('password').value;
if(getcookie('svnupdate') != passwordInputValue){
setcookie('svnupdate',passwordInputValue,365);
}
}
//Cookie操作函数
function setcookie(name,value,days){
if("undefined" == typeof(days)){
days = 30;
}else{
days = parseInt(days);
}
var exp = new Date();
exp.setTime(exp.getTime() + days*24*60*60*1000);
document.cookie = name + "="+ value + ";expires=" + exp.toGMTString();
}
function getcookie(name){
var arr = document.cookie.match(new RegExp("(^| )"+name+"=([^;]*)(;|$)"));
if(arr != null){
return (arr[2]);
}else{
return "";
}
}
</script>
</form>
<?php
if($_POST['password'] !='123456'){
die('密码错误');
}
putenv("LC_CTYPE=zh_CN.UTF-8");
$handle = popen('svn up /test --username zmk --password 123456 2>&1', 'r');
$read = stream_get_contents($handle);
echo "<pre>";
printf($read);
echo "</pre>";
pclose($handle);
?>
这样就实现了测试服务器的自动更新,线上服务器则通过访问http://mengkang.net/update.php来手动更新代码。
代码以演示为主,大家可以在脚本中添加svn更新出错发送邮件等扩展功能。
别忘了这些代码是建立在svn搭建完毕之后,测试服务器和线上服务器都做了一次检出之上的。
线上服务器的update.php代码与测试服务器上的代码相似,可以做一些安全防护措施,比如加一个限定和密码输入。
在上面这些代码都部署完毕之后,可能会因为各种权限问题,导致不能正常自动更新,你可以update.php页面,那上面输出各种错误的原因。
有一个坑需要提醒下:
在svn 1.6 以后的版本之后,当然执行svn up的时候会默认提示是否需要保存密码,需要修改下配置文件才可能跳过这步。
假设你的svn的配置文件在这/home/www/.subversion/servers
请编辑下它,全局密码保存配置修改为如下规则
[global]
store-passwords = yes
store-plaintext-passwords = no
不管是测试服务器还是线上服务器都应该拒绝对所有.svn目录的访问。
location ~ ^(.*)\/\.svn\/{
deny all;
}
类似的,如果测试服务器在外网必须设置为只有开发人员的ip才能访问。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。