團隊開發配合SVN HOOKS同步更新在線服務器源碼

最近為了協調整個開發團隊的開發過程,讓團隊協作更加流暢,考慮減少開發人員更新代碼維護代碼的中間環節,於是考慮使用了SVN服務端HOOKS

SVN服務端的 HOOKS 存在於每個 Project 倉庫的hooks目錄,裏面的 *.tmpl 是hooks腳本模板,我使用的是 windows 來做內網源碼版本 服務器系統,所以使用 *.bat 批處理文件來做 Hooks腳本! 但是總所周知 *.bat 批處理文件能執行的任務有限,所以我的批處理只是吧參數引導到PHP腳本上,由PHP腳本來實現我所需要的結果。
一、首先提高 svn 服務程序的權限,修改 "服務" 中 SVN 服務程序的登錄帳戶為超級管理員
二、編寫批處理。因為我只是需要在開發人員提交腳本後執行我的任務,所以只編寫了 post-commit.bat 批處理引導到了PHP腳本 post-commit.php上

CODE:

@echo off
setlocal
SET PHP=E:\PHP5\php.exe
%PHP% -f %1\hooks\post-commit.php %*

關於hooks腳本,各位可以看這裏 http://www.subversion.org.cn/svn ... #svn.ref.reposhooks

三、建立 post-commit.php 在每次提交試執行判斷是否需要執行同步操作
我這裏加了 SVN 賬戶判斷,以便只有特定的用戶才有權限向線上服務器同步源碼
還加了[UPTO_RUN_SERVER] 標記,只有特定的用戶在提交時,加入了這個標記才會觸發同步到在線服務器的工作

四、建立一個臨時目錄用於從版本倉庫抽取需要更新的文件,如:E:\svn_tmp\MY_PROJECT_NAME

五、至於如何在團隊協同開發中部署這個應用,或如何更合理有效和安全,各位可以發揮一下想象力

以下是 post-commit.php 代碼的主要部分,有BUG僅供參考

[php]
<?php
//-----------------------------
// 初始化設置
//-----------------------------

//臨時更新目錄,用於svn checkout 得到最新的文件後,上傳到FTP
TMP_UPDATE_DIR = "E:\\svn_tmp\\MY_PROJECT_NAME";

//獲得當前更新的log
svnlook_log = "\"C:\\Program Files\\VisualSVN Server\\bin\\svnlook.exe\" log -r {argv[2]} {argv[1]}";

//獲得當前更新者帳戶名
svnlook_author = "\"C:\\Program Files\\VisualSVN Server\\bin\\svnlook.exe\" author -r {argv[2]} {argv[1]}";

//更新最新的源碼到臨時更新目錄
svn_update = "\"C:\\Program Files\\VisualSVN Server\\bin\\svn.exe\" update -r {argv[2]} {TMP_UPDATE_DIR}";

//有權限更新到運行服務器的svn帳戶
upto_run_managers = array('terry39', 'user02', 'user03', 'user04');

//更新狀態記錄文件
update_status_file = "E:\\svn_tmp\\MY_PROJECT_NAME_update_status.txt";

output = array();
exec(svnlook_log, output);
log = output;

output = array();
exec(svnlook_author, output);
author = output;


//-----------------------------
// 函數
//-----------------------------


/**
* 在FTP服務器上創建目錄
*
* @author terry39
*/
function ftp_create_dir(path)
{
global ftp;

dir=split("/", path);
path="";
ret = true;

for (i=0;i<count(dir);i++)
{
path.="/".dir[i];
if(!@ftp_chdir(ftp,path)){
@ftp_chdir(ftp,"/");
if(!@ftp_mkdir(ftp,path)){
ret=false;
break;
}
}
}
return ret;
}

/**
* 刪除 FTP 中指定的目錄
*
* @param resource ftp_stream The link identifier of the FTP connection
* @param string directory The directory to delete
*
* @author terry39
*/
function ftp_rmdirr(ftp_stream, directory)
{
if (!is_resource(ftp_stream)
get_resource_type(ftp_stream) !== 'FTP Buffer') {

return false;
}

// Init
i = 0;
files = array();
folders = array();
statusnext = false;
currentfolder = directory;

// Get raw file listing
list = ftp_rawlist(ftp_stream, directory, true);

foreach (list as current) {

if (empty(current)) {
statusnext = true;
continue;
}

if (statusnext === true) {
currentfolder = substr(current, 0, -1);
statusnext = false;
continue;
}

split = preg_split('[ ]', current, 9, PREG_SPLIT_NO_EMPTY);
entry = split[8];
isdir = (split[0]{0} === 'd') ? true : false;

// Skip pointers
if (entry === '.' entry === '..') {
continue;
}

if (isdir === true) {
folders[] = currentfolder . '/' . entry;
} else {
files[] = currentfolder . '/' . entry;
}

}

foreach (files as file) {
ftp_delete(ftp_stream, file);
}

rsort(folders);
foreach (folders as folder) {
ftp_rmdir(ftp_stream, folder);
}

return ftp_rmdir(ftp_stream, directory);
}

/**
* 把更新到的文件列表上傳到服務器上
*
* 更新的同時記錄更新日志,如過有一個文件或者目錄更新失敗,則返回錯誤
*
* @param array update_list
* @return bool
*
* @author terry39
*/
function update_to_ftp(update_list)
{
global argv, ftp, author_name, TMP_UPDATE_DIR;

ftp_update_logfile='E:\svn_tmp\ftp_MY_PROJECT_NAME_upload.log'; //執行更新的日志文件
ftp_root_dir = '/'; //服務器上的地址源碼對應的起始目錄

log = "";
log .= date('Y-m-d H:i:s') . " BEGIN UPDATE TO FTP\n";
log .= date('Y-m-d H:i:s') . " Reversion: {argv[2]}\n";
log .= date('Y-m-d H:i:s') . " Author: {author_name}\n";

ftp = ftp_connect('FTP服務器地址');
ftp_login = ftp_login(ftp, 'FTP賬戶', 'FTP密碼');

if(ftp && ftp_login){
log .= date('Y-m-d H:i:s') . " Connected to FTP Server Success\n";
}else{
log .= date('Y-m-d H:i:s') . " Connected to FTP Server False\n";
log .= date('Y-m-d H:i:s') . " UPDATE FALSE\n";
log .= date('Y-m-d H:i:s') . " QUIT UPDATE\n\n";
return false;
}

result = true;
foreach(update_list as file_cmd){
if(substr(file_cmd, 0, 6) == 'Update') continue; //這裏有最後一行的 Update Reversion NNN 要忽略
file_cmd = trim(file_cmd);
cmd = substr(file_cmd, 0, 1);
file = trim(substr(file_cmd, 1));
from = file;
file = substr(file, strlen(TMP_UPDATE_DIR) + 1); //去掉路徑中的開頭 TMP_UPDATE_DIR 路徑
filename = is_dir(from) ? null : array_pop(explode('/', file));
to = ftp_root_dir . str_replace("\\", "/", file);

//計算出路徑並創建FTP目錄 (如果不存在的話)
dir = is_dir(from) ? to : dirname(to);
if(!@ftp_chdir(ftp,dir)){
log .= date('Y-m-d H:i:s') . " FTP_MKD\t{dir}\n";
ftp_create_dir(dir); //創建目錄
}
if(is_dir(from)) continue;

//更新或創建文件
if(cmd == "U" cmd == "A"){
result = result && ftp_put(ftp, to, from, FTP_BINARY);
//記錄日志
log .= date('Y-m-d H:i:s') . " FTP_PUT\t{from}\t{to}" . (result ? "\tSUCCESS\n" : "\tFALSE\n");

//刪除文件或目錄
}else if(cmd == "D"){
//-------------------------------
// 這裏要判斷目標是目錄還是文件
//-------------------------------

if(@ftp_chdir(ftp,to)){
r = ftp_rmdirr(ftp, to);
log .= date('Y-m-d H:i:s') . " FTP_RMD\t{to}" . (r ? "\tSUCCESS\n" : "\tFALSE\n");
}else{
result = result && ftp_delete(ftp, to);
//記錄日志
log .= date('Y-m-d H:i:s') . " FTP_DEL\t{to}" . (result ? "\tSUCCESS\n" : "\tFALSE\n");
}
}else{
log .= date('Y-m-d H:i:s') . " UNKNOWN CMD\t{cmd}\n";
continue;
}
}
//記錄最後一次更新成功的版本
if(result){
log .= date('Y-m-d H:i:s') . " UPDATE SUCCESS\n";
}else{
log .= date('Y-m-d H:i:s') . " UPDATE FALSE\n";
}
log .= date('Y-m-d H:i:s') . " END UPDATE\n\n";
file_put_contents(ftp_update_logfile, log, FILE_APPEND);

return result;
}

//-----------------------------
// 判斷並執行同步更新
//-----------------------------


//取得當前更新者帳戶名,判斷是否有權限更新到運行服務器
author_name = author[count(author) -1];
if(in_array(author_name, upto_run_managers)){

//查看log中是否包含 [UPTO_RUN_SERVER] 指令標記
if(strpos(implode("\n", log), '[UPTO_RUN_SERVER]') !== false){

//-------------------------------------------
// 准備開始同步更新在線服務器上的文件
//-------------------------------------------

new_rev = argv[2];


if(!file_exists(update_status_file)){
file_put_contents(update_status_file, '11');
}
update_status = explode('', file_get_contents(update_status_file));

//當記錄的更新狀態為更新成功的版本號{update_status[0]} 比最後一次更新的版本號 {update_status[1]} 小時
//還原臨時更新目錄到最後一次更新成功的版本 {update_status[0]},然後再 update 獲得更新文件列表
//然後提交到在線服務器

if(update_status[0] < update_status[1]){
svn_update_r = "\"C:\\Program Files\\VisualSVN Server\\bin\\svn.exe\" update -r {update_status[0]} {TMP_UPDATE_DIR}";
exec(svn_update_r);
}

output = array();
exec(svn_update, output);
update = output;

//這裏根據update列表更新服務器上的文件
update_success = true;
update_success = update_to_ftp(update);

//記錄更新後的更新狀態到到文件
if(update_success){
file_put_contents(update_status_file, "{new_rev}{new_rev}");
}else{
file_put_contents(update_status_file, "{update_status[0]}{new_rev}");
}
}
}
?>
[/php]

文章標籤
全站熱搜
創作者介紹
創作者  銘 的頭像

銘的部落格

發表在 痞客邦 留言(0) 人氣(160)