Jenkins进阶系列之——08Jenkins纳入版本控制

 2014-12-08:已不再担任SCM和CI的职位,Jenkins系列的文章如无必要不会再维护。

2014-07-25:更新shell脚本

2014-06-05:更新shell脚本

2014-01-09:更新shell脚本,修改Jenkins文件删除后不能自动从版本控制删除的bug

是不是有过这种经历:某天手贱,把某一个配置更改了。然后只能在那儿苦逼的看着,然后的然后把以前的配置重新配一次。坑爹的是以前配置的什么都忘记了。。。尼玛,坑啊( ⊙ o ⊙ )!

某天在群里和一群人吹牛逼,突然冒出一个人说手贱把某个job删除了,有没有办法恢复。然后我们果断的告诉了ta实情。然后这聊天的主题就跑到把jenkins加入到版本控制中。我也曾经手贱过。运气好,前几天要测试一些东西,在另一台机器上面还有备份。所以折腾折腾,就写了这篇文章。

系统:CentOS 5.8、6.4

版本控制软件:Subversion 1.7+

思路:写一个脚本(shell)定时去跑一次add、delete和commit。

  1. 建立普通用户
    用一个用户会出问题,jenkins构建的时候会直接报错。

     这个问题的解决方法:将用户目录下.subversion的auth目录删除就可以了

    useradd froad    #建立普通用户跑Jenkins,root用户用了跑shell脚本
    #启动Jenkins略过
    #因为我是用root用户登录,然后 su froad切换的,所以就没有设置密码。
  2. 安装Subversion1.7以上版本(当做客户端给root用户使用)
    。其实有用的就是bin/svn这个程序而已。如果你安装了Subversion edge 直接把bin和lib目录拷贝过来就行了。为什么要用1.7以上的?因为1.7后.svn目录只有一个了。我嫌以前的版本.svn目录太多了,烦!提供个解压即可用的svn(从subversion edge中拷贝出来的) 点我去下载
  3. 远程Subversion的一些准备工作

    如果你喜欢可以创建一个新的库和用户,如果你不like,随便你!

  4. 检出svn目录
    [root@localhost ~]# /home/froad/svn/bin/svn co https://192.168.xxx.xxx/svn/Jenkins/trunk /home/froad/.jenkins/  --username jenkins
    #说明:/home/froad/.jenkins这个参数可以直接把检出的.svn目录放到.jenkins目录,我是懒人不想在mv一次。--username是svn的用户
    #svn语法:svn co url path --username xxx
    Error validating server certificate for 'https://192.168.xxx.xxx:443':
     - The certificate is not issued by a trusted authority. Use the
       fingerprint to validate the certificate manually!
     - The certificate hostname does not match.
    Certificate information:
     - Hostname: froad-jskfb
     - Valid: from Apr 16 03:04:08 2013 GMT until Apr 14 03:04:08 2023 GMT
     - Issuer: (null), (null), (null), (null), (null) ((null))
     - Fingerprint: D6:EB:58:xxxxxxxxxxxxxxxFC:7D:04:64
    (R)eject, accept (t)emporarily or accept (p)ermanently? p #说明:R 拒绝 t 暂时接受 p 永久接受
    Authentication realm: <https://192.168.xxx.xxx:443> VisualSVN Server
    Password for 'jenkins': ************* #说明:输入你的用户密码
    -----------------------------------------------------------------------
    ATTENTION!  Your password for authentication realm:
    
       <https://192.168.xxx.xxx:443> VisualSVN Server
    
    can only be stored to disk unencrypted!  You are advised to configure
    your system so that Subversion can store passwords encrypted, if
    possible.  See the documentation for details.
    
    You can avoid future appearances of this warning by setting the value
    of the 'store-plaintext-passwords' option to either 'yes' or 'no' in
    '/root/.subversion/servers'.
    -----------------------------------------------------------------------
    Store password unencrypted (yes/no)? yes #说明:存储未加密的密码
    Checked out revision 1.
    
    
    #现在版本库都检出了,下面的就简单了。add一下,然后commit一下。打完收工。
    #如果你是这么想的,那么结果会郁闷死你。接着往下看吧。

  5.  更改Jenkins的workspace目录

     为什么要更改workspace目录呢?因为里面有个捣蛋的目录.svn。这个目录是个深坑,开始我花了大力气去忽略这个目录。坑爹的是,如果我把工作目录清空了,然后在构建就会一直报错。因为SVNkit跑去找JENKINS_HOME下的.svn目录(这个目录就是我们上面检出的)。

    测试的时候是直接把.svn目录检出到test_007ka目录。大家将就看。除了目录不一样,其他没有区别。

     SVNkit会先去找.svn目录,如果到顶层目录还是没有找到.svn目录,就会在workspace目录检出。这是svn和git的一个特性。哪儿看到的?那个《git权威指南》。
    系统管理→系统设置→主目录(的右边问号下面)→高级(是不是忽略了啊\(^o^)/~)→工作空间根目录

        点开后面的问号可以看见3个参数(配置路径需要的):
        • ${JENKINS_HOME} — Jenkins home directory.#JENKINS_HOME这个参数不用说了
        • ${ITEM_ROOTDIR} — Root directory of a job for which the default workspace is allocated.#ITEM_ROOTDIR:默认的工作空间目录。完整的路径就是JENKINS_HOME/jobs/xxxx/workspace 
        • ${ITEM_FULL_NAME} — '/'-separated job name, like "foo/bar".#ITEM_FULL_NAME:job的名称,这个就是我们需要的。

     我们只需要把workspace目录赶出JENKINS_HOME目录就行了。上配置:

    workspace:/home/froad/workspace/${ITEM_FULL_NAME} #前面的目录随便你改,只需要在最后带上${ITEM_FULL_NAME}
    JENKINS_HOME:/home/froad/.jenkins #给你们对比着看
    #好了,这下.svn目录不打架了。我也不用头疼了。
  6. 删除已经存在的workspace目录

    find . -type d -name "workspace"|xargs rm -rf  #看见find后面的那个点了么,改成你的路径就行了
  7. 配置Subversion忽略目录

     貌似将JENKINS_HOME目录直接提交到Subversion,数据量不是一般的高啊!我这个Jenkins才跑3个月都10Gb了。

     如果要提交上去,花费的时间,不是一般的长啊。

     注意:你用的什么用户,就在用户目录下面改。我用的root用户。

     进入用户目录的.subversion打开config文件,找到global-ignores。大概在105行。

     把global-ignores前的#去掉,注意#后面的空格要删掉。然后在=后面添加 modules

     modules目录是maven项目产生的,我的Jenkins就是这个目录大。把这个目录删除了也就几百Mb。如果你有其他目录想忽略,请自行添加,记得用空格分隔就行了。

  8. 写个shell脚本svn_commit.sh

     这个脚本随便你放在哪儿,我想偷懒。直接放在Jenkins的主目录,顺便也版本控制下。。O(∩_∩)O~
     声明:脚本写的很丑,勿喷!

    #!/bin/bash
    
    # ------------------------------------------------------
    # Jenkins的Subversion备份脚本
    # 请将本脚本放到JENKINS_HOME目录
    # 作者:zjl
    # version:4.1
    # time:2014-07-25 17:36
    #
    
    # 更新日志
    # version:4.1
    # 修护删除文件导致版本过时提交失败的bug
    
    # version:4.0
    # 修护从${TMP_STATUS_LOG}文本读取一行,变量前空格丢失的bug
    # 修护从${TMP_STATUS_LOG}文本截取文件名某些情况错误的bug
    # 优化脚本对特殊路径的处理
    # 添加svn commit失败邮件告警功能
    # 删除add 、commit时强制指定的用户
    
    
    # version:2.0
    # 修改Jenkins文件删除后不能自动从版本控制删除的bug
    
    # 检出命令:svn co https://192.168.xxx.xxx/svn/Jenkins/trunk /home/froad/.jenkins/  --username jenkins
    
    #
    # ------------------------------------------------------
    
    
    # 获取当前系统时间分割日志文件(如果错误时邮件内容过大,可以将时间写的更精确)
    # 精确到分钟
    # DATE=`date "+%Y%m%d%H%M"`
    # 精确到小时
    DATE=`date "+%Y%m%d%H"`
    
    # SVN_HOME的路径
    SVN_HOME="/home/froad/svn"
    
    # SVN程序的路径
    SVN="${SVN_HOME}/bin/svn"
    
    # JENKINS_HOME的路径
    JENKINS_HOME="/home/froad/jenkins"
    
    # 邮件告警设置
    # 收件人(多个收件人用空格或逗号分隔)
    MAIL_TO="xxx@qq.com"
    # 伪造发件人(不能和收件人的邮件后缀相同,否则发送不了邮件)
    MAIL_FROM="jenkins_svn@163.com"
    # 主题/标题
    MAIL_SUBJECT="[ERROR]Jenkins SVN commit fail !"
    # 邮件发送类型
    # 0:通用普通,163、QQ等邮箱会识别成垃圾邮件
    # 1:Centos 6.x Mail version 12.4 7/29/08.
    # 2:Centos 5.x Mail version 8.1 6/6/93.
    # 请根据自己的Mail版本选择使用
    MAIL_TYPE="1"
    
    # 日志文件
    LOG_HOME="/root/.subversion/logs"
    
    LOG_FILE="${LOG_HOME}/svn_commit_${DATE}.log"
    
    # svn_status日志文件
    TMP_STATUS_LOG="${LOG_HOME}/status.log"
    
    
    # svn_add 方法已停止使用,由svn_status代替。
    #function svn_add()
    #{
        #使用svn add命令将文件添加到版本控制
    #    "${SVN}" add  "${JENKINS_HOME}/*"   --force &>>"${LOG_FILE}"
        #退出码
    #    EXIT_NUM=$?
    #    echo "svn add退出码:${EXIT_NUM} !" >>"${LOG_FILE}"
    #}
    
    function send_mail(){
    
        # 检查邮件参数
        # 收件人为空
        if [ -z "${MAIL_TO}" ];then
    
            echo "[error]MAIL_TO 参数为空!">>"${LOG_FILE}"
            return
    
        fi
        # 伪造发件人发送时,发件人为空
        if [ "${MAIL_TYPE}"=="1" -o "${MAIL_TYPE}"=="2" ];then
    
            if [ -z "${MAIL_FROM}" ];then
    
                echo "[error]MAIL_FROM 参数为空!">>"${LOG_FILE}"
                return
    
            fi
        fi
    
        # LC_ALL=zh_CN.UTF-8 保证发送邮件时中文不会乱码
        # mail相关请查看 http://ju.outofmemory.cn/entry/24406
    
        if [ "${MAIL_TYPE}"=="1" ] ;then
            # 1:Centos 6.x Mail version 12.4 7/29/08. 
            LC_ALL=zh_CN.UTF-8 mail -s "${MAIL_SUBJECT}" -r "${MAIL_FROM}" "${MAIL_TO}"  < "${LOG_FILE}"
    
        elif [ "${MAIL_TYPE}"=="2" ] ;then
    
            # 2:Centos 5.x Mail version 8.1 6/6/93. 
            LC_ALL=zh_CN.UTF-8 mail -s "${MAIL_SUBJECT}" "${MAIL_TO}" -- -f "${MAIL_FROM}" < "${LOG_FILE}"
    
        else
    
        # 0:通用普通,163、QQ等邮箱会识别成垃圾邮件
        # 注意:此方法可能会被163等邮箱识别为垃圾邮件
            LC_ALL=zh_CN.UTF-8 mail -s "${MAIL_SUBJECT}"  "${MAIL_TO}" < "${LOG_FILE}"
    
        fi
    }
    
    function svn_status()\
    {
    
        # 使用svn status命令判断文件是否删除、修改或者其他操作
        "${SVN}" status  "${JENKINS_HOME}" > "${TMP_STATUS_LOG}"
    
        # 从文件中读取一行
        cat "${TMP_STATUS_LOG}" | while read line;do
    
            # 从文本中读取回来的内容必须用引号,不然前面的空格会丢失
            # 获取第一个字符,根据第一个字符判断情况
            l=`echo "${line}"|cut -c1`
            # 获取文件的相对路径(获取第8个字符后的所有内容,前6个字符是svn的状态,2个字符间隔)
            # 详细状态请查询:http://www.subversion.org.cn/svnbook/nightly/svn.ref.svn.c.status.html
            f=`echo "${line}" | cut -c9-`
            
            # 状态为A(增加)时,不需要任何操作,直接返回。
            if [ "${l}" == "A"  ];then
                continue
            fi
    
            # 状态为D(删除)时,不需要任何操作,直接返回。
            if [ "${l}" == "D"  ];then
                continue
            fi
    
            # 状态为M(修改)时,不需要任何操作,直接返回。
            if [ "${l}" == "M"  ];then
                continue
            fi
    
            # 状态为I(忽略)时,不需要任何操作,直接返回。
            if [ "${l}" == "I"  ];then
                continue
            fi
    
            # 状态为?(未受控制)时,调用add命令然后返回。ps:不知道怎么的有时候从文件中读取回来的?变成了0
            if [ "${l}" == "0" ] || [ "${l}" == "?"  ];then
    
                "${SVN}" add  "${JENKINS_HOME}/${f}" >>"${LOG_FILE}"
    
                continue
    
            fi
    
            # 状态为!(丢失或者不完整)时,调用delete命令然后返回。
            if [ "${l}" == "!"  ];then
    
                "${SVN}" delete "${JENKINS_HOME}/${f}" >>"${LOG_FILE}"
    
                continue
    
            fi
    
            echo "[info]其他情况:${line}" >>"${LOG_FILE}"
    
        done
    
        # 删除临时文件
        # rm -rf ${TMP_STATUS_LOG}
    
    }
    
    function svn_update()
    {
        echo "svn update ...">>"${LOG_FILE}"
    
        "${SVN}" update &>>"${LOG_FILE}"
    
        svn_commit
    
    }
    
    function svn_commit()
    {
    
        # 使用svn commit命令提交到服务器
        "${SVN}" commit  --message="crontab commit" &>>"${LOG_FILE}"
    
        EXIT_NUM=$?
    
        if [ ! ${EXIT_NUM} == "0" ];then
    
        if [ -z ${ISOUTOFDATE} ]  ;then
        # 确保这个if只执行一次
        ISOUTOFDATE="1"
        # 判断是否需要更新后在尝试提交(默认查找最后10行,-n xx指定行数)
            isUpdate=`tail "${LOG_FILE}"  | grep "is out of date"`
            echo "[debug]+:${isUpdate}"
            if [ ! -z "${isUpdate}" ];then
                svn_update
                return
            fi
        fi
            echo "[error]退出码:${EXIT_NUM} svn commit失败,请查看日志!">>"${LOG_FILE}"
    
            # 调用发送mail方法
            send_mail
    
            return
    
        fi
    
        echo "svn commit退出码:${EXIT_NUM} !">>"${LOG_FILE}"
    
    }
    
    
    echo "[begin] 当前时间:`date "+%Y%m%d_%H%M%S"`" >>"${LOG_FILE}"
    
    if [ ! -d "${LOG_HOME}" ];then
    
        mkdir -p "${LOG_HOME}"
    
    fi
    
    # 进入到svn工作目录,防止出现稀奇古怪的错误
    cd "${JENKINS_HOME}"
    
    # 调用方法
    svn_status
    svn_commit
    
    echo "[end] 当前时间:`date "+%Y%m%d_%H%M%S"`" >>"${LOG_FILE}"
    
    # 输出空行隔离
    echo  >>"${LOG_FILE}"

     

     

     

     

    脚本写好了,建议先运行一次。测试下有没有错误。

  9.  添加定时提交
    crontab -u root -e
    */30 * * * * sh /home/froad/.jenkins/svn_commit.sh #每30分钟提交一次
  10.  一个备份插件

    Jenkins Job Configuration History Plugin,可以记录配置的更改。可以记录谁,什么时间,修改了什么。

  11. 结尾

    普通用户开机自动启动Jenkins:

    su 用户名 -c "sh path"
    #例如:su froad -c "sh /usr/local/jenkins/bin/startup.sh"
    将上面的命令加入到/etc/rc.d/rc.local文件中

    非root用户不能使用1024以下的端口,比较麻烦。有不懂的,下面留言。有好的建议,下面留言。欢迎交流!

posted @ 2013-09-27 10:04  爱自己  阅读(...)  评论(...编辑  收藏