團隊開發配合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, '1|1');
        }
        $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) 人氣()