Git团队开发中PR工作模式的反思
Tag git, pr, 工作流, on by view 3279

在公司团队协作用了大半年的Git了,PR工作模式也用了半年。由一个用Git装逼的假老手成长为真正用Git进行团队协作开发的老油条。这其中也有一些值得反思的东西。我很崇拜PR的工作模式,因为它基本上是优秀的开源软件工作模式的代表,众多的开发者自发的给开源项目发送PR,以前总是盼望着自己的开源项目会有人发送PR,若是接收到PR就像是收到别人的礼物一般高兴。

我之前赞同的PR工作模式是这样的,开发者拿到开发需求之后,在自己的分支开发,然后向主仓库的相关分支发送PR,之后由测试人员在测试机上拉取主仓库相关分支的代码,然后fetch PR所在仓库分支的代码合并(merge)到本地主仓库相关分支,进行测试,测试完毕通过之后才合并PR到分支。我这一想法的灵感来源于 travis-ci 单元测试的工作模式:开发人员发送PR,travis-ci自动进行单元测试,PR管理者参考PR在 travis-ci 上单元测试的结果进行初步判定是否可以合并。但是我忽略了一点,测试人员并非是 travis-ci 上的单元测试。在工作的过程中往往是这样的:

开发人员初步开发出某个功能,然后经过自己的初步测试,发送PR到主仓库,测试人员看到有PR了,需要测试了,于是开始测试,但是测试有问题,于是开发人员就必须继续修改提交,然后再次测试,还有一些问题被发现依然存在,继续开发……可是,你突然发现,你的PR被合并了或者是被关闭了,因为要下班了;或者是要下班了,PR管理员会问这个PR能否合并,测试人员暂时没发现新的问题,于是测试人员告诉PR管理员“没有问题”,于是PR被合并了。或者没有上面的“PR管理员的询问”,但不管怎样,最后的结果往往是PR被草草的合并了。于是,没有充分测试的PR进入了仓库,甚至进入了发布分支,最终的结果可想而知。

在这个过程中,人们往往没有意识到其实是被PR给影响到了。因为开发者一个PR创立之后,在他会有一个潜意识希望PR被合并,测试人员同样也有这么一个潜意识,因为测试PR就是他的工作之一,他也希望PR被合并,对于PR管理员来说同样也有这么一个潜意识,希望PR被处理,不管是合并还是关闭。就是这些潜意识推动这个PR尽早被合并。

我个人感觉正确的做法应该是当测试人员测试充分之后再发起PR,避免这些潜意识促使PR过早的被合并。事实上,不管PR是否存在,测试人员都能够从开发者的分支上拉取到开发者的代码合并到主仓库,PR不过是一个形式而已,给合并者带来方便。但是那些错误的理解了PR的人们往往会搞出诸多莫名其妙的东西,比如PR用来人工做代码审核,比如搞个CheckList让人工去查代码中哪儿写得不规范,这些应该是让 Sonar 自动扫描PR中存在的不规范,谁又会有时间去为别人的PR人工做Sonar该做的事情。经过测试人员的充分测试之后,开发者创建PR,这时单元测试测试一遍,然后Sonar扫描一遍,后两者自动化测试通过之后代码才能入库。

总之,测试人员功能测试通过之后开发者才能创建PR,自动化工具进行单元测试和Sonar扫描通过后,PR管理员才能允许合并。这才是比较靠谱的PR工作流。


windows live writer与wordpress通讯协议(xmlrpc)分析
Tag windows live writer, wordpress, xmlrpc, 通讯, on by view 5743

最近一直在完善自己的博客,wordpress博客不可不谓之博客系统中最完善的一个,我的博客系统也一直以wordpress为样本,向wordpress学习。相信用过wordpress的人都用过windows live writer作为自己博客的客户端。windows live writer原本是微软为live博客开发的客户端,但是现在live博客已经关停,因此windows live writer也停止了更新。作为windows系统下最佳的博客离线客户端,即便它已经停止更新,它也将会是一款经典的软件,如同xp系统一样。

最近两天的时间,我将自己的博客系统实现了与windows live writer通讯,苦于找不到有用的指导资料,折腾得很辛苦。

调试工具:
服务端:本地搭建wordpress,我的博客blog
客户端:windows live writer,chrome浏览器的Postman应用扩展

调试过程:

  1. 进入本地wordpress首页,找到xmlrpc协议入口

    <link title="RSD" rel="EditURI" type="application/rsd+xml" href="http://127.0.0.1/xmlrpc.php">

    之后的所有请求都是从这个入口上实现的。

  2. 协议检查。windows live writer连接后的第一件事请是获取到首页上的接口(title为RSD的link标签),第二件事情便是向http://127.0.0.1/xmlrpc.php发送get请求,获取到的内容大致如下:

    <?xml version="1.0" encoding="UTF-8"?><rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">
      <service>
        <engineName>WordPress</engineName>
        <engineLink>http://wordpress.org/</engineLink>
        <homePageLink>http://127.0.0.1</homePageLink>
        <apis>
          <api name="WordPress" blogID="1" preferred="true" apiLink="http://127.0.0.1/xmlrpc" />
        </apis>
      </service>
    </rsd>
  3. 登录,发送blogger.getUsersBlogs请求【以下请求的主体均在http request的body内,并且皆为post请求】

    <?xml version="1.0" encoding="utf-8"?>
    <methodCall>
     <methodName>blogger.getUsersBlogs</methodName>
     <params>
      <param>
       <value>
        <string>ffffffabffffffce6dffffff93ffffffac29ffffffc9fffffff826ffffffdeffffffc9ffffffe43c0b763036ffffffa0fffffff3ffffffa963377716</string>
       </value>
      </param>
      <param>
       <value>
        <!-- 用户名 -->
        <string>lijun</string>
       </value>
      </param>
      <param>
       <value>
        <!-- 密码 -->
        <string>lijun</string>
       </value>
      </param>
     </params>
    </methodCall>

    响应:

    <?xml version="1.0" encoding="UTF-8"?>
    <methodResponse>
      <params>
        <param>
          <value>
            <array>
              <data>
                <value>
                  <struct>
                    <!-- 1 -->
                    <member><name>isAdmin</name><value><boolean>1</boolean></value></member>
                    <!-- homepage -->
                    <member><name>url</name><value><string>%s</string></value></member>
                    <!-- userid -->
                    <member><name>blogid</name><value><string>%d</string></value></member>
                    <!-- blogname -->
                    <member><name>blogName</name><value><string>%s</string></value></member>
                    <!-- xmlrpc url address -->
                    <member><name>xmlrpc</name><value><string>%s</string></value></member>
                  </struct>
                </value>
              </data>
            </array>
          </value>
        </param>
      </params>
    </methodResponse>

    上面的%d%s等按照自己的博客替换成相应的返回参数,遵循printf函数的格式化规则。

  4. 新建文章metaWeblog.newPost

    <?xml version="1.0" encoding="utf-8"?>
    <methodCall>
     <methodName>metaWeblog.newPost</methodName>
     <params>
      <param>
       <value>
        <string>1</string>
       </value>
      </param>
      <param>
       <value>
        <string>lijun</string>
       </value>
      </param>
      <param>
       <value>
        <string>lijun</string>
       </value>
      </param>
      <param>
       <value>
        <struct>
         <member>
          <name>title</name>
          <value>
           <string>test tag</string>
          </value>
         </member>
         <member>
          <name>description</name>
          <value>
           <string>&lt;p&gt;test tag content&lt;/p&gt;</string>
          </value>
         </member>
         <member>
          <name>mt_text_more</name>
          <value>
           <string />
          </value>
         </member>
         <member>
          <name>wp_slug</name>
          <value>
           <string />
          </value>
         </member>
         <member>
          <name>mt_basename</name>
          <value>
           <string />
          </value>
         </member>
         <member>
          <name>wp_password</name>
          <value>
           <string />
          </value>
         </member>
         <member>
          <name>categories</name>
          <value>
           <array>
            <data>
             <value>
              <string>ceshi</string>
             </value>
             <value>
              <string>tag</string>
             </value>
            </data>
           </array>
          </value>
         </member>
         <member>
          <name>mt_excerpt</name>
          <value>
           <string />
          </value>
         </member>
        </struct>
       </value>
      </param>
      <param>
       <value>
        <boolean>1</boolean>
       </value>
      </param>
     </params>
    </methodCall>

    响应:

    <?xml version="1.0" encoding="UTF-8"?>
    <methodResponse>
      <params>
        <param>
          <value>
          <!-- post id -->
          <string>%d</string>
          </value>
        </param>
      </params>
    </methodResponse>
  5. 新建分类标签请求wp.newCategory

    <?xml version="1.0" encoding="utf-8"?>
    <methodCall>
     <methodName>wp.newCategory</methodName>
     <params>
      <param>
       <value>
        <string>1</string>
       </value>
      </param>
      <param>
       <value>
        <string>lijun</string>
       </value>
      </param>
      <param>
       <value>
        <string>lijun</string>
       </value>
      </param>
      <param>
       <value>
        <struct>
         <member>
          <name>name</name>
          <value>
           <string>测试</string>
          </value>
         </member>
         <member>
          <name>parent_id</name>
          <value>
           <int>0</int>
          </value>
         </member>
        </struct>
       </value>
      </param>
     </params>
    </methodCall>

     响应:

    <?xml version="1.0" encoding="UTF-8"?>
    <methodResponse>
      <params>
        <param>
          <value>
          <!-- catalog id -->
          <int>%d</int>
          </value>
        </param>
      </params>
    </methodResponse>
  6. 新建媒体文件metaWeblog.newMediaObject【图片上传】

    <?xml version="1.0" encoding="utf-8"?>
    <methodCall>
     <methodName>metaWeblog.newMediaObject</methodName>
     <params>
      <param>
       <value>
        <string>1</string>
       </value>
      </param>
      <param>
       <value>
        <string>lijun</string>
       </value>
      </param>
      <param>
       <value>
        <string>lijun</string>
       </value>
      </param>
      <param>
       <value>
        <struct>
         <member>
          <name>name</name>
          <value>
           <string>loadfailed.png</string>
          </value>
         </member>
         <member>
          <name>type</name>
          <value>
           <string>image/png</string>
          </value>
         </member>
         <member>
          <name>bits</name>
          <value>
           <!-- base64转码的图片文件 -->
           <base64>iVBORw0KGgoAAAANSUhEUgAAAkJggg==</base64>
          </value>
         </member>
        </struct>
       </value>
      </param>
     </params>
    </methodCall>

     响应:

    <?xml version="1.0" encoding="UTF-8"?>
    <methodResponse>
      <params>
        <param>
          <value>
          <struct>
            <!-- file id -->
            <member><name>id</name><value><string>%d</string></value></member>
            <!-- file name -->
            <member><name>file</name><value><string>%s</string></value></member>
            <!-- file url -->
            <member>
              <name>url</name>
              <value><string>%s</string></value>
            </member>
            <!-- file type -->
            <member><name>type</name><value><string>%s</string></value></member>
          </struct>
          </value>
        </param>
      </params>
    </methodResponse>


协议的内容太多,如有需要还建议自己使用相关的工具进行捕捉请求,以及使用Postman捕捉响应,然后自己分析请求与响应的内容,并在自己的系统中实现它。响应部分的其他内容可以参考我的博客系统源码http://github.com/duguying/blog ,请求部分我已经打包可以直接下载


写在网站变迁之后
Tag 网站, 备案, wordpress, go, 博客, on by view 5811

最近几天一直忙于这学期的课程设计作业,无法抽空写点什么。其实这次博客变动之后早就想写点东西记录一下本次博客搬迁的过程。

本次博客搬家是从原先的位于英国的Hostinger空间搬迁至国内的阿里云服务器,搬迁的原因主要有两个:第一个原因是国内访问国外的服务器线路极其的不稳定,经常被和谐或者是访问速度很慢;第二个原因是我已经决定并开始使用go语言重构了博客系统,弃用原来的基于php的wordpress博客系统,这一次用go语言重构博客也是一个挑战。

博客变迁记载

  • 2011-2012年左右,博客是基于php的wordpress系统,曾放置于“浦东信息港”那个免费空间提供商,相比于国内的免费空间这一家还算不错(不排除有我不知道的更好的),他提供香港机房版免备案空间,同时也提供单个MySQL数据库(不是整个MySQL服务器),可是有两个缺点:访问速度慢;不支持curl服务(这意味着不能在线安装wordpress的各种插件)。

  • 2013年,我申请了域名 duguying.net ,使用修改版的wordpress将网站放置于百度云,并且绑定了域名,可是不久之后百度云开始检查备案,未备案的网站一律停止域名解析,再到后来百度云升级了,可是升级后的系统却是非常的不方便使用,在到后来百度云开始大张旗鼓的收费了,感觉再也不爱百度云了。

  • 2014年上半年,将网站迁移至Hostinger,之前百度云上面的数据有部分已经丢失了,可是好景不长,Hostinger免费又是国外的免备案服务器,估计注册的人不少,那么各种言论也不少,相信触动了某的逆鳞的言论也是不少,因此就会偶尔遭遇和谐,对于这种各网站公用ip的空间来说,一个网站被和谐意味着相同ip的所有网站空间全部被和谐,空间提供商是老外,别人可不在意什么和谐。另外,Hostinger经常会出现CPU过载,然后你的网站就会自动跳转到一个警告页面,对于这个表示无语。最后,网速的确很差。

  • 2014年下半年,手痒了,想搞一个云服务器,把网站全部迁移至拥有独立造作系统、拥有root账户的云服务器或者是VPS。本人一开始是表示不喜欢备案的,因为我并不认为我的言论会导致什么坏的事情所以觉得自己没必要被审查,然后早就听说备案特别复杂,是一件很费神的事情。于是,我对比了国内外VPS/云服务器的价格之后,决定先在DigitalOcean购买一个月的VPS试用一下。买下之后却遇到了一系列的问题(将帐号锁定禁止开通主机),各方谷歌之后发现甚至有人大骂DigitalOcean野蛮终止自己的服务器导致大量的客户丢失(链接),硬着头皮看完英文,我想糟了该不会是被老外坑了吧,最后还是决定通过沟通的方式看能不能解决,于是我给客服发送用我那蹩脚的英文写的电子邮件,邮件中描述了我所遇到的状况并表示希望能够解除锁定,等待了半日的时间,终于收到了回复的电邮,帐号也解锁了。于是,立即创建了一个虚拟主机,ssh登陆之,最后发现ssh竟然卡出翔……我在putty上敲了一行的命令,没反应,大概过了十来秒屏幕突然一下子又出现了刚才敲的一行命令,按删除键没反应,再按又没反应,十来秒后一下子删除了N个字符,删多了。这样的用户体验,谁能忍!毕竟是国外的服务器……

  • 2014年7月左右,DigitalOcean那卡出翔的用户体验让我放弃了继续试用linode,听说linode可以选择日本的数据中心,网速还不错,据说大神byvoid的个人博客就是放在linode,可是又据说这个日本的数据中心容易被和谐。于是,恰逢遇到阿里云促销“0元半年体验”,申请成功后发现这个是不包括网络带宽的,没办法,一咬牙就花了127大洋买了6个月的带宽。ssh登录,速度很不错,速度上感觉与本地虚拟机相似比较爽,没办法了,买了国内的云服务器,乖乖备案吧。

  • 2014年8月,既然买了一个独立的云服务器那么就得用到实处,我决定使用go语言重构博客了,第一周看了《Go语言编程》那本书的大部分,第二周开始基于Beego框架构建博客系统,第三周博客后台完成大部分,开始构思前端界面并且开始准备备案的事情。终于要备案了,这是我第一次备案,备案使用的是阿里云系统,阿里云的客服处理事情的速度还是比较快的,提交初审后过了两小时就有客服妹纸打电话说明相关信息并且告诉我我起得网站名称不行,需要改一下更容易通过,于是就按照客服妹纸的建议改为“大俊的个人网站”(我提交的名称是“大俊哥之家”),并且客服告诉我我的域名持有人名称与申请备案人名称不一致,当时域名是在oray注册的,注册信息填写英文名的时候填写的是Rex Lee,因此显示为Rex Lee,客服建议我讲域名转到阿里云。后来我发现阿里云和万网居然合并了(万网被阿里云收购),记得我上次登录万网时候那时还没有合并,域名价格都是特贵的,所以当初才选择了相对便宜的Oray,可是现在一看,域名居然白菜价,转入39,注册49,续费55,我果断决定转入了,可是,有另外一件麻烦事情,当初Oray帐号注册时候没有实名认证,Oray规定只有实名认证的用户才能够转出域名,没办法,当初注册帐号填写的是企业账户,我现在是无法实名认证的,最终决定注册一个小号,将域名在帐号间转移(花了我10块大洋),然后将小号实名认证,Oray的客服效率真心查啊,帐号实名认证花了4天,这还是我在他官方bbs上催他们才有这效率的,终于,实名认证成功了,也成功获取了域名转移密码,申请转入万网成功,然后又等了将近5个工作日,终于成功转过来了,在原域名商那儿还有5个月的时间也一同转过来了,这是我没想到的。最后发现,国际域名.net根本就不支持中文持有人信息Orz...不过我还是决定将Rex Lee改为中文名的汉语拼音。域名转入的事情告一段落了,与此同时,我申请了阿里云备案的幕布,自己用他的幕布拍照,传照片以及各种资料,然后快递资料。接下来便是漫长的等待了,据说20个工作日可以得到备案结果,还好没用20天,备案号终于发下来了。早已经准备好的网站系统终于可以启动了,我霸气的在putty中敲下service nginx start启动了前端服务器。网站开通。

折腾了这么久,网站总算是通了,我想以后我会继续续费,将这个网站继续下去,go语言才刚刚开始,我也是刚刚开始学习go语言,后面的路还很长,我看好golang。也会坚持写博客。