<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
 <channel>
  <title>草色天涯</title>
  <link>http://dear.blogbus.com</link>
  <description><![CDATA[我是李剑，ThoughtWorks咨询师，现居北京。目前主要关注于思考自己应该关注啥。]]></description>
  <generator> by blogbus.com </generator>
  <lastBuildDate>Thu, 10 May 2012 23:52:02 +0800</lastBuildDate>
  <image>
									<url>http://public.blogbus.com/profile/7/0/2/15207/avatar_15207_96.jpg</url>
									<title>草色天涯</title>
									<link>http://dear.blogbus.com</link>
								</image>  <item>
   <title>JavaScript的prototype</title>
   <description><![CDATA[<p>在《JavaScript, The Good Parts》一书中，作者这样写到:</p>
<blockquote>Every object is linked to a prototype object from which it can inherit
properties. </blockquote>
<p>于是，如果我们试图从一个对象中获取属性，而该对象上缺少这个属性，它就会查找自己的prototype对象，如果这个prototype对象上还没有，然后就会继
续往prototype对象的prototype对象上找，一直找到根上--- Object.prototype。
这种prototype的关系是动态的，一旦给某个prototype上加了一个属性，在所有基于此prototype的对象上就都能看到这个属性。</p>
<p>利用这个特性可以完成很多事情，比如添加公共方法和动态的提供对象上的方法增强。</p>
<p>《JavaScript, The Good Parts》这本书中对于第一点给出了很多例子，例如实现Function的curring</p>
<pre>Function.prototype.method = function(method, func){
	this.prototype[method] = func; 
	return this;
};

Function.method('curry', function(){
	var slice = Array.prototype.slice,
		args = slice.apply(arguments),
		that = this;
	return function(){
		return that.apply(null, args.concat(slice.apply(arguments)));
	};
});
</pre>
<p>&nbsp;</p>
<p>用Jspec给它写个测试看看:</p>
<pre>describe 'method should be able to be curried'
  it 'should curry add method'
	var add = function(number1, number2) {
		return number1 + number2;
	}
	
    var curried_add = add.curry(7)
	curried_add(8).should_be(15)
  end
end
</pre>
<p>&nbsp;</p>
<p>同理，我们也可以自己给Array上增加一些便利的方法，比如select:</p>
<pre>describe 'functional test'	
	before_each
		function People(name){
			this.name = name;
		}
		
		friends = [new People(&quot;jacky&quot;), new People(&quot;xiaodao&quot;), new People(&quot;jane&quot;)]
	end
	
	it 'should do select'
		var jane = friends.select(function(people) { if (people.name == &quot;jane&quot;) return people; })
		jane.name.should_be(&quot;jane&quot;);
	end
end
</pre>
<p>&nbsp;</p>
<p>实现代码如下:</p>
<pre>Array.method('select', function(f){
	var i;
	for(i = 0; i &lt; this.length ; i+=1){
		var result = f(this[i]);
		if( result != null){
			return result; 
		}
	}
});
</pre>
<p>&nbsp;</p>
<p>也可以把《Scala程序设计》中第七章讲隐式类型转换的例子重写一下: </p>
<pre>Number.method('days', function(when){
	var date = new Date();
	switch(when){
		case &quot;ago&quot;:
			date.setTime(date.getTime() - this * 3600 * 1000 * 24);
			break;
		case &quot;after&quot;:
			date.setTime(date.getTime() + this * 3600 * 1000 * 24);
			break;	
		default:
	};
	return date;
});
</pre>
<p>&nbsp;</p>
<p>然后就可以这样用了: var date2DaysAgo = (2).days(&quot;ago&quot;);</p><!--sp--><div class="addfav"><br />收藏到：<span class= "delicious"><a href="http://delicious.com/save?url=http%3A%2F%2Fdear.blogbus.com%2Flogs%2F104074581.html&title=JavaScript%E7%9A%84prototype">Del.icio.us</a></span></div><br /><br /><div class="sysmsg"><b><a href="http://www.blogbus.com" target="_blank">博客大巴，你的个人传媒早班车</a></b></div><br /><br />]]></description>
   <link>http://dear.blogbus.com/logs/104074581.html</link>
   <author>dearwolf</author>
   <pubDate>Thu, 10 Feb 2011 08:59:00 +0800</pubDate>
  </item>
  <item>
   <title>可测试性驱动的Javascript重构</title>
   <description><![CDATA[<h2>引子</h2>
<p>&nbsp;</p>
<p>项目里面曾经有这样一段遗留代码，嵌在了jspx页面里面：</p>
<pre>function getDropDownOptions(data) {
	$.ajax({
		url: &quot;/dropdownoptions.json&quot;,
		dataType: &quot;json&quot;,
		cache: false,
		data: data,
		success: function(data) {
			$(&quot;#journals&quot;).html(&quot;&quot;);
			for (idx in data.journals) {
			    $(&quot;#journals&quot;).append(&quot;&quot; + data.journals[idx].name + &quot;&quot;);
			}
			$(&quot;#journals&quot;).removeAttr(&quot;disabled&quot;);
		}
	});
}

$(document).ready(function() {
	$(&quot;#series&quot;).change(function() {
		$(&quot;#journals&quot;).attr(&quot;disabled&quot;, &quot;disabled&quot;);
		$(&quot;#journals&quot;).html(&quot;Loading journals...&quot;);
		getDropDownOptions({
			&quot;journalType&quot;: $(&quot;#series&quot;).val()
		});
	});
});
</pre>
<p>实现的功能很简单，就是个级联下拉框，第一个下拉框#series被选中以后，第二个下拉框#journals先被禁用掉，等异步请求返回数据之后，再把数据填充到下拉框里面，并将之使能。</p>
<p>看上去代码还是很好读懂的，但测试却是个大问题。后来就发现了<a href="http://visionmedia.github.com/jspec/">jspec</a>

这个好东西，于是我想，那就把这段代码重构一下，然后加上测试吧。</p>
<h2>开工</h2>
<p>&nbsp;</p>
<p>要给一段遗留代码补测试，首先要弄明白要测试的功能是什么。上面这陀代码实际上干了三件事情: 1. 禁用#journals下拉框；2. 发起ajax请求；3. 拿到返回的json对象，用它填充#journals下拉框。也可以归为两类，一类是发送ajax请求，一类是操作dom。我想先把操作dom的这部分功能加上测试，比如禁用下拉框(dropdownmodel_spec.js):</p>
<pre>describe 'dropdownModel'
  	before_each
	    html = $(fixture('dropdownlist.html'))
	    dropdownModel = new DropdownModel(html)
  	end

  	it 'should prepare for series change'
    	   dropdownModel.prepare()

	   journals = html.find(&quot;#journals&quot;)
	   journals.should.be_disabled
	   journals.find(&quot;option&quot;).should.have_text('Loading journals...')
  	end
end
</pre>
<p>&nbsp;</p>
<p>在上面的代码里，首先是用fixture()这个方法构建了一个fixture，把dom传给DropdownModel的构造参数，让js和html解耦。fixture的加载路径是在测试页面中配置的，可参考jspec文档。然后是DropdownModel：</p>
<pre>function DropdownModel(dom){
	this._dom = dom;
}

DropdownModel.prototype = {	
	prepare : function(){
		 var journals = this._dom.find(&quot;#journals&quot;);
		 journals.attr(&quot;disabled&quot;, &quot;disabled&quot;);
		 journals.html(&quot;Loading journals...&quot;);
	}
}
</pre>
<p>&nbsp;</p>
<p>但测试页面显示有failure，这是因为fixture页面还没有写好，这里写个最简单的页面就好了，只要带有id为journals的select元素就行。测试通过以后，我们可以继续把用json数据填充下拉框的测试也写完。先构造一个json字符串:</p>
<pre>{
&quot;journals&quot; : [
	{
	&quot;id&quot; : &quot;0&quot;,
	&quot;name&quot; : &quot;All journals in series&quot;
	},	{
	&quot;id&quot; : &quot;3009&quot;,
	&quot;name&quot; : &quot;Alzheimer's Research&quot;
	}]
}
</pre>
<p>&nbsp;</p>
<p>然后是测试:</p>
<pre>it 'should populate journals'
	journal_json = fixture('journal.json')
	journal_data = eval('('+ journal_json +')')

  	dropdownModel.populate(journal_data)
	
	journals = html.find(&quot;#journals&quot;)		
	journals.should.have_many 'option'
	journals.find(&quot;option&quot;)[0].should.have_text(&quot;All journals in internal series&quot;);
	journals.find(&quot;option&quot;)[1].should.have_text(&quot;Alzheimer's Research&quot;);
end
</pre>
<p>&nbsp;</p>
<p>实现就略过不提了。接下来就是测试发送ajax请求的部分，这个类对外只有一个接口，比如叫做changeSeries吧，它要先调用model的prepare方法，然后发请求，然后把json对象做参数，调用model的populate方法。JSpec提供了mock ajax request的方法，于是测试也变得很简单了。比如:</p>
<pre>describe 'heading'
  	before_each
	    html = $(fixture('dropdownlist.html'))
	    model = new DropdownModel(html);
	    controller = new DropdownController(model);
  	end
	
	describe 'change series'		
	   it 'should call model populate journals after a success ajax call'
	       journal_json = fixture('journal.json')
	       mock_request().and_return(journal_json, 'application/json');
		
		controller.changeSeries();

		journals = html.find(&quot;#journals&quot;)		
		journals.should.have_many 'option'
		journals.find(&quot;option&quot;)[0].should.have_text(&quot;All journals in internal series&quot;);
		journals.find(&quot;option&quot;)[1].should.have_text(&quot;Alzheimer's Research&quot;);
	    end
	end
end
</pre>
<p>&nbsp;</p>
<p>实现代码如下:</p>
<pre>function DropdownController(model){
	var _model = model;
	
	this.changeSeries = function(){
		 _model.prepare();

		 getDropDownOptions({
		 	&quot;journalType&quot;: $(&quot;#series&quot;).val()
		 });
	}
	
	function getDropDownOptions(data) {
		var model = _model;
		$.ajax({
			url: &quot;dropdownlist.json&quot;,
			dataType: &quot;json&quot;,
			cache: false,
			data: data,
			success: function(data) {
				model.populate(data)
			}
		});
	}
}
</pre>
<p>&nbsp;</p>
<h2>结束语</h2>
<p>&nbsp;</p>
<p>到此为止，原先嵌在页面中的一段js代码已经分离了出来，并根据职责的不同，拆分出了两个类，一个操作dom，一个处理ajax请求，并且有了针对操作dom行为的两个单元测试，以及针对ajax请求的一个集成测试(如果愿意的话，还可以mock一下model的prepare方法，测试它会被controller的changeSeries方法调用到)。</p>
<p>在这个过程中，我的感触是:</p>
<p>1. JSpec是很牛逼的。提供了如rspec一样清爽的语法；有很大一陀功能强大，也更易读的Matchers，一如hamcrest；有before_each和after_each用；支持JQuery；原生支持mock ajax call；可以用selenium把js测试集成到CI里面去</p>
<p>2. Javascript的可测试性是很重要的，这样才能用低成本的单元测试、集成测试来覆盖javascript的功能，而不是成本高昂的功能测试，或者是傻傻的一味加断点Debug。</p>
<p>(完)</p><!--sp--><div class="addfav"><br />收藏到：<span class= "delicious"><a href="http://delicious.com/save?url=http%3A%2F%2Fdear.blogbus.com%2Flogs%2F103809731.html&title=%E5%8F%AF%E6%B5%8B%E8%AF%95%E6%80%A7%E9%A9%B1%E5%8A%A8%E7%9A%84Javascript%E9%87%8D%E6%9E%84">Del.icio.us</a></span></div><br /><br /><div class="sysmsg"><b><a href="http://www.blogbus.com" target="_blank">博客大巴，你的个人传媒早班车</a></b></div><br /><br />]]></description>
   <link>http://dear.blogbus.com/logs/103809731.html</link>
   <author>dearwolf</author>
   <pubDate>Tue, 08 Feb 2011 17:11:00 +0800</pubDate>
  </item>
  <item>
   <title>两天没提交代码了</title>
   <description><![CDATA[<p>昨天是因为本地环境有问题，到了要提交代码的时候发现本地跑单元测试跑不过。今天是因为某个agent出现了问题，导致CI一直红。<br />

<br />

现在的情况是整个team公用一个Codebase，只有一个负责跟遗留系统做集成的团队在分支上工作（据说从前有过特性分支）。本地改动稍微一多，提交代码的频率一慢，就有可能pull下别人的代码来，于是就要跑所有的单元测试，一跑就半个来小时，顺利通过的话再上CI。CI上的pipeline是这样子的，有一个prePackage的stage，打好包，剩下的所有stage就全用这一个打好的包来跑，然后大概是有十个agent分布式跑Acceptance Test这个pipeline，同时有十个agent跑unit test这个pipeline。CI跑完一遍大概也要一个来小时，如果不红的话。<br />

<br />

在这种情况下，可以想象到整个团队每天可以提交多少次代码，以及每次提交的难度有多大了。。。<br />

<br />

基于这样的情况，团队采用了令牌机制，每个人想提交代码之前，先申请令牌，然后跑本地测试，通过后提交代码，等CI的提交构建。CI通过之后归还令牌。如果CI红了，并且预计在短时间内无法修复的话，就revert代码，归还令牌。<br />

<br />

令牌缓解了团队协作的问题，但另一个问题依然存在。这个问题可以用下面的图来表示：</p>
<p><a href="http://www.bababian.com/phoinfo/37DBF5DB24F9615EEFB07E016366D30DDT" target="_blank"><img style="border:none;" src="http://photo2.bababian.com/upload5/20110119/37DBF5DB24F9615EEFB07E016366D30D_500.jpg" alt="" />

</a>

</p>
<p>图中的S表示同向变化，O代表反向变化。</p>
<p>针对这种问题，我们常常采用的实践就是分布式、分阶段、分层。</p>
<p>分布式无庸多言。</p>
<p>分层可以理解为按特性划分支，在特性分支中的构建（包括本地和CI），都只跑跟这个特性有关的测试，与之同时，分支和主干之间，必须要频繁（每日）同步，在分支向主干push的时候，要跑主干上的所有测试，在从主干向分支pull的时候，要跑跟分支有关的测试。</p>
<p>这是用来解决团队规模过大，特性之间相互影响的问题。这个实践在团队中曾经用过一段时间，为了不影响某个特性的critical release，专门拉了一条分支出去。每天做同步。但release完了以后分支就干掉了。<br />

<br />

分阶段是用来解决构建时间过长的问题。其解决方案是，让一部分价值最高，成功率最高，运行时间短的测试作为提交构建，每次提交的时候，都必须要跑这些测试。在这个基础上，把全部的测试作为全量构建，放到不会有人频繁提交代码的时候运行，防止对工作造成影响。执行的策略是专人轮流负责，红了必须修。</p>
<p>全量构建的通过用来评价提交构建的好坏，如果全量构建经常失败，就把全量构建中的一些测试放到提交构建里面去做；提交构建里面一些价值不高的测试，也可以挪到全量构建里面去。<br />

<br />

但在执行分阶段集成或者分层集成策略的时候，每个人也必须意识到，这些策略都是有成本的，是在一定条件下达成的妥协。它所带来的好处是可以快速反馈，但问题是这种反馈是不是真实的？在反馈中发现的问题是否容易修复？实践证明，当全量构建、Trunk上的构建失败的时候，已经远离了问题出现的时机，定位问题、修复问题的代价都增加了。这就是成本，也是风险。如果对全量构建的关注度不够、分支主干的同步频率很低&hellip;&hellip;最终的结果可能就是无法在主干上得到一个稳定构建，最终放弃并回到苦苦等待令牌的老路上来。</p><!--sp--><div class="addfav"><br />收藏到：<span class= "delicious"><a href="http://delicious.com/save?url=http%3A%2F%2Fdear.blogbus.com%2Flogs%2F100691046.html&title=%E4%B8%A4%E5%A4%A9%E6%B2%A1%E6%8F%90%E4%BA%A4%E4%BB%A3%E7%A0%81%E4%BA%86">Del.icio.us</a></span></div><br /><br /><div class="sysmsg"><b><a href="http://www.blogbus.com" target="_blank">博客大巴，你的个人传媒早班车</a></b></div><br /><br />]]></description>
   <link>http://dear.blogbus.com/logs/100691046.html</link>
   <author>dearwolf</author>
   <pubDate>Wed, 19 Jan 2011 17:15:00 +0800</pubDate>
  </item>
  <item>
   <title>我们都在风雨之中慢慢成长，谁记得当初脸上青涩模样</title>
   <description><![CDATA[<p>记得去年5月11号，项目第一个迭代结束的时候，我在日记中写到：</p>
<div id=":1ya">
<div style="margin-left: 40px;">整体感觉很无力。</div>
<div style="margin-left: 40px;"><br />
</div>
<div style="margin-left: 40px;">几十个project组成的遗留系统，改点东西要翻天覆地的找代码；无数的存储过程，繁琐的手工的deploy、
release步骤；需求不清晰，客户非抵抗不合作。<br />
<br />
迭代的起止没有任何明确的标志。show 
case是下个迭代的第一天做，回顾是下个迭代的第二天做。没有kickoff meeting。计划在哪里，在mingle上。<br />
<br />
但情况也一点点变好，渐渐熟悉了遗留系统、IIS和Tomcat的结合、部署流程。自动化脚本也增加了起来。<br />
</div>
</div>
<div id=":1ya"><br />
大半年过去了，回头看看前三个迭代的回顾墙，不免五味杂陈。<br />
<br />
我们基本上不会再出现一个story做一个迭代的盛况了；<br />
<br />
也不需要每次迭代都要反复提及&ldquo;提前做spike，分一个pair专门做spike&rdquo;之类的话题了；<br />
<br />
一开始发布domain的时候要瞪着屏幕瞪10分钟，后来有了Autoit的脚本，到后来domain也不需要发布了；<br />
<br />
后来有了自动化功能测试，然后又没了，最后又有了；<br />
<br />
代码删啊删，测试加啊加，有个项目的覆盖率超过80%了；<br />
<br />
某个机器上应该还有inception之前搭起来的本地持续集成吧，那上面还有dbdeploy + 
sqlplus的自动化数据migration呢。客户的tech lead离开北京的时候答应说把这套东西在伦敦搞好，可是到他离职的那天，还没在mingle上建卡呢；<br />
<br />
该走的客户还没走，不该走的客户却走了；从一开始就对客户的协作态度不满意，回顾的Action上写着&ldquo;PM催；PM继续催；PM想办法催；PM找CP催&rdquo;，但一直也没成功过；<br />
<br />
有的人来了就没有走；有的人来了又走了，然后就再没回来过；</div>
<div><br />
</div>
<div>快要2011年了，更快要离开项目了，只想说一句话：</div>
<div><br />
<strong>回头望，每个人都看那世事无常<br />
向前闯，一颗心比谁都还要坚强</strong>
</div><!--sp--><div class="addfav"><br />收藏到：<span class= "delicious"><a href="http://delicious.com/save?url=http%3A%2F%2Fdear.blogbus.com%2Flogs%2F95127283.html&title=%E6%88%91%E4%BB%AC%E9%83%BD%E5%9C%A8%E9%A3%8E%E9%9B%A8%E4%B9%8B%E4%B8%AD%E6%85%A2%E6%85%A2%E6%88%90%E9%95%BF%EF%BC%8C%E8%B0%81%E8%AE%B0%E5%BE%97%E5%BD%93%E5%88%9D%E8%84%B8%E4%B8%8A%E9%9D%92%E6%B6%A9%E6%A8%A1%E6%A0%B7">Del.icio.us</a></span></div><br /><br /><div class="sysmsg"><b><a href="http://www.blogbus.com" target="_blank">博客大巴，你的个人传媒早班车</a></b></div><br /><br />]]></description>
   <link>http://dear.blogbus.com/logs/95127283.html</link>
   <author>dearwolf</author>
   <pubDate>Fri, 31 Dec 2010 16:11:00 +0800</pubDate>
  </item>
  <item>
   <title>贴心而恼人的WinApps</title>
   <description><![CDATA[<p>WinApps是个相当有爱的应用，它可以让你直接在Mac上启动某些Windows应用程序，比如迅雷，比如Raysource。我常常需要从Raysource上下载资源，但每次为了下载百兆甚至几十兆或者几兆的文件就要开个虚拟机，这让我实在不堪其扰。</p>
<p>还好有了WinApps，于是我就可以体面的在Mac上用Raysource了。但它又带来了新的困扰。一如绝大多数下载工具一样，它即便在退出以后也在后台放一个常驻进程，这一点在Windows还没有太大问题，但在Mac上退出一次以后就再也启动不起来了。。。WTF！</p>
<p>作为一名体面的程序员，我无法接受每次都去ps aux | grep Raysource 再kill进程这样的操作，于是写了这样的脚本[raysource.rb]：</p>
<p>raysource_status = `ps aux|grep peer.exe`.split(&quot;\n&quot;)<br />
raysource_process = raysource_status.select{|line| <br />
&nbsp; line.split(&quot; &quot;).select{|piece| piece.eql? 'Files\RaySource\peer.exe'}.length != 0 <br />
}<br />
if raysource_process.length != 0 then<br />
&nbsp; raysource_process_id =&nbsp; raysource_process[0].split(&quot; &quot;)[1]<br />
&nbsp; `kill #{raysource_process_id}`<br />
end</p>
<p>然后我只需要再写一个shell脚本，quitraysource.sh，给QuickSilver添加一个Trigger指向 &quot;quitraysource&quot;+&quot;Run in Terminal...&quot;，以后每次想退出Raysource的时候，用快捷键调用那个Trigger就可以了。</p>
<p>从发现Raysource的问题到写完程序花了大概半个来小时，但以我平均两天使用一次Raysource的频率而言，它可以为将来节省大量手工重复操作的时间。</p>
<p>我不知道迅雷、纳米人，乃至IE是不是也存在同样的问题，但如果你曾遇到过的话，希望这个程序对你有用。不知道如何配置QuickSilver Trigger的朋友，可以参看我从前的博客：<a href="http://dear.blogbus.com/logs/60783334.html" target="_blank">QuickSilver进阶应用指南</a>
</p><!--sp--><div class="addfav"><br />收藏到：<span class= "delicious"><a href="http://delicious.com/save?url=http%3A%2F%2Fdear.blogbus.com%2Flogs%2F90179442.html&title=%E8%B4%B4%E5%BF%83%E8%80%8C%E6%81%BC%E4%BA%BA%E7%9A%84WinApps">Del.icio.us</a></span></div><br /><br /><div class="sysmsg"><b><a href="http://www.blogbus.com" target="_blank">博客大巴，你的个人传媒早班车</a></b></div><br /><br />]]></description>
   <link>http://dear.blogbus.com/logs/90179442.html</link>
   <author>dearwolf</author>
   <pubDate>Fri, 17 Dec 2010 23:36:00 +0800</pubDate>
  </item>
  <item>
   <title>培养全功能团队</title>
   <description><![CDATA[<p>在TW西安办公室有这样一支团队，他们近一年来没有加过班，一直在维护风险墙，一直在消除风险。</p>
<p>他们项目中每个刚毕业的大学生都跟国外客户谈过需求。</p>
<p>在项目结束的时候，他们培养出了一个能写程序的测试，两个能做测试的dev，一个能管项目的dev，一个能做UI的dev。</p>
<p>想知道这样一支团队是如何炼成的，请期待<a href="www.iamhukai.com">胡凯</a>
的文章完稿。</p><!--sp--><div class="addfav"><br />收藏到：<span class= "delicious"><a href="http://delicious.com/save?url=http%3A%2F%2Fdear.blogbus.com%2Flogs%2F83841177.html&title=%E5%9F%B9%E5%85%BB%E5%85%A8%E5%8A%9F%E8%83%BD%E5%9B%A2%E9%98%9F">Del.icio.us</a></span></div><br /><br /><div class="sysmsg"><b><a href="http://www.blogbus.com" target="_blank">博客大巴，你的个人传媒早班车</a></b></div><br /><br />]]></description>
   <link>http://dear.blogbus.com/logs/83841177.html</link>
   <author>dearwolf</author>
   <pubDate>Mon, 15 Nov 2010 09:01:00 +0800</pubDate>
  </item>
  <item>
   <title>AutoIt，让开发的日子更简单</title>
   <description><![CDATA[<p>平时常常需要打开Mingle看故事卡的内容：需求描述、AC等等。每次都要开一个新窗口，输地址等待自动补全，把自动补全的地址改成需要的number，回车。这一系列的操作是手动而且重复的。按照《卓有成效程序员》的说法，是典型的应该被自动化的环节。</p>
<p>于是有了这些脚本。</p>
<p>Firefox.au3</p>
<blockquote>$firefoxClass = "[CLASS:MozillaUIWindowClass]"<br />$firefoxOpened = WinExists($firefoxClass)<br />$firefoxExecutable = "C:\Program Files\Mozilla Firefox\firefox.exe"<br /><br />Func run_firefox()<br />&nbsp;&nbsp; &nbsp;If $firefoxOpened Then<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;active_firefox()<br />&nbsp;&nbsp; &nbsp;Else<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;open_firefox()<br />&nbsp;&nbsp; &nbsp;EndIf<br />EndFunc<br /><br />Func active_firefox()<br />&nbsp;&nbsp; &nbsp;WinActivate($firefoxClass)<br />EndFunc<br /><br />Func open_firefox()<br />&nbsp;&nbsp; &nbsp;Run($firefoxExecutable)<br />EndFunc<br /><br />Func wait_util_firefox_opened()<br />&nbsp;&nbsp; &nbsp;WinWaitActive($firefoxClass)<br />EndFunc<br /><br />Func open_new_tab()<br />&nbsp;&nbsp; &nbsp;Send("^t")<br />&nbsp;&nbsp; &nbsp;Sleep(500)<br />EndFunc</blockquote>
<p>MingleCard.au3</p>
<blockquote>#include &lt;firefox.au3&gt;<br />$cardnumber = $CmdLine[1]<br /><br />run_firefox()<br />wait_util_firefox_opened()<br />open_new_tab()<br />enter_card_url()<br /><br />Func enter_card_url()<br />&nbsp;&nbsp; &nbsp;Send("http://localhost:8080/projects/projectname/cards/")<br />&nbsp;&nbsp; &nbsp;Send($cardnumber)<br />&nbsp;&nbsp; &nbsp;Send("{ENTER}")<br />EndFunc</blockquote>
<p>show.bat</p>
<blockquote>minglecard.au3 %1</blockquote>
<p>
把show.bat加入PATH以后，只需要输入show 20这样的命令，就可以直接打开序号为20的这张卡了。</p><!--sp--><div class="addfav"><br />收藏到：<span class= "delicious"><a href="http://delicious.com/save?url=http%3A%2F%2Fdear.blogbus.com%2Flogs%2F68224187.html&title=AutoIt%EF%BC%8C%E8%AE%A9%E5%BC%80%E5%8F%91%E7%9A%84%E6%97%A5%E5%AD%90%E6%9B%B4%E7%AE%80%E5%8D%95">Del.icio.us</a></span></div><br /><br /><div class="sysmsg"><b><a href="http://www.blogbus.com" target="_blank">博客大巴，你的个人传媒早班车</a></b></div><br /><br />]]></description>
   <link>http://dear.blogbus.com/logs/68224187.html</link>
   <author>dearwolf</author>
   <pubDate>Mon, 05 Jul 2010 17:53:28 +0800</pubDate>
  </item>
  <item>
   <title>Scrum Master是否需要技术背景？</title>
   <description><![CDATA[<p>InfoQ上发表了这篇新闻：<a href="http://www.infoq.com/cn/news/2010/06/technical-scrummaster" target="_blank">Scrum Master是否需要技术背景</a></p>
<p>在敏捷中国论坛里我曾说过：</p>
<blockquote>
<p>流程专家首先必须是生产专家，这是被丰田重复过无数次的话了。</p>
<p>不懂生产就不知道哪里是现场，<br />
即便知道哪里是现场可能也看不懂，<br />
即便能看懂现场也未必看得到问题，<br />
即便看得到问题也未必能知道问题的根源，<br />
即便知道问题的根源也提不出解决方案。</p>
这样的人跑到团队里边来指手画脚不就是一件令人愤怒的事情么。</blockquote>
<p>这次看到Bob大叔给了个很中肯的评价：</p>
<blockquote>Scrum does not define any technical disciplines so, strictly speaking, Scrum Masters do not need to be technical. However, if technical disciplines (like TDD, Continuous Integration, Refactoring, etc.) are not used, then the code produced by the scrum team will very quickly rot into a festering pile, leaving the team entombed in an ever deepening and thickening tar pit. Therefore _effective_ scrum teams must be good at technical practices; and someone must play coach for those practices. That technical coach might be the scrum master, or it might be the lead developer.</blockquote>
<p>
但总是会看到有人出来扯看上去很美的淡：
</p>
<blockquote>根本问题并非&ldquo;Scrum Master 是否需要技术背景？&rdquo;，而是进行敏捷实施的团队是否需要相关的技术指导，
如果团队没有技术指导方面的需求，Scrum Master 自然无需太多的技术背景！
敏捷开发是为了随需应变，而不流于形式；
敏捷实施也应如此，Scrum Master 应各有所长、各司其职！</blockquote><!--sp--><div class="addfav"><br />收藏到：<span class= "delicious"><a href="http://delicious.com/save?url=http%3A%2F%2Fdear.blogbus.com%2Flogs%2F67673372.html&title=Scrum+Master%E6%98%AF%E5%90%A6%E9%9C%80%E8%A6%81%E6%8A%80%E6%9C%AF%E8%83%8C%E6%99%AF%EF%BC%9F">Del.icio.us</a></span></div><br /><br /><div class="sysmsg"><b><a href="http://www.blogbus.com" target="_blank">博客大巴，你的个人传媒早班车</a></b></div><br /><br />]]></description>
   <link>http://dear.blogbus.com/logs/67673372.html</link>
   <author>dearwolf</author>
   <pubDate>Thu, 01 Jul 2010 11:02:09 +0800</pubDate>
  </item>
  <item>
   <title>五月书单</title>
   <description><![CDATA[<p>1. 《<a href="http://www.amazon.cn/gp/product/B0017HCC1K/ref=oss_product" target="_blank">六顶思考帽</a>》</p>
<p>在用系统反馈图的方式来剖析问题成因、改善点、观测点的时候，有一点相当重要，那就是要头脑风暴。每个人都有自己的知识局限性、认知差异，所以有的人会摸到象腿，有的人会摸到身子，或者尾巴。只有每个人都可以无阻碍的把自己的理解呈现出来，这个团队才能建立一个趋向于分析系统全貌的<span class="il">思考</span>方式。</p>
<div class="ii gt">《<span class="il">六</span><span class="il">顶</span><span class="il">思考</span><span class="il">帽</span>》跟《系统<span class="il">思考</span>》有很多异曲同工之处，但它给出了一种更具有可操作性的团队协作方式。<br />
<br />书的开篇就指出：因为每个人各自的关注点不同，再加上背景和知识结构的差异，往往会出现很多不必要的争论。而这些争论丝毫无益于得出一个建设性的共识，无益于团队中的智力资源整合。<br /><br />作者提出的解决方案很简单：所有<span class="il">思考</span>者在同一时间只做一件事情，把逻辑、情感、创造、信息等等区分开。我们有<span class="il">六</span><span class="il">顶</span>不同颜色的<span class="il">帽</span>子，戴上不同颜色的<span class="il">帽</span>子，表示换一个看问题的角度。<br /><br />通过这种方式，我们可以更多的关注于&ldquo;我们能够做什么&rdquo;，而不是谁对谁错。</div>
<div class="ii gt"><br /></div>
<div class="ii gt">2. 《软件随想录──Joel谈软件》</div>
<div class="ii gt"><br /></div>
<div class="ii gt">前两天荣浩说起他在看微观经济学，我随口问了句谁推荐的，然后荣浩、晓庆和我三个异口同声说，&ldquo;Joel&rdquo;。Joel是个很有煽动力的八卦er，至少在我这个没有把<span class="Yd"><span class="ze">1940年以来的重要论文或者图灵演讲集都读过一遍的人眼中看来，他还是很能把各种史事信手拈来挥洒如意的。</span></span></div>
<div class="ii gt"><br /></div>
<div class="ii gt"><span class="Yd"><span class="ze">3. 《<a href="http://www.amazon.cn/gp/product/B001PTGXVU/ref=oss_product" target="_blank">常识</a>》</span></span></div>
<div class="ii gt"><br /></div>
<div class="ii gt"><span class="Yd"><span class="ze">常识是本很沉重的书，作者在封底说，&ldquo;如果时事评论的目的是改变现实，那么现实的屹立不变就是对它的最大嘲讽了&rdquo;。然而正如某<a href="http://losersclub1958.thoughtworkers.org/" target="_blank">历史系女博士</a>所说，</span></span><span id="col-z13qvj1zzxebsvq5o04chxfimkucjxdxkrg0k" class="z19Dle"><span class="zo">&ldquo;人不会变得更好，他们只是变得更聪明。人变聪明之后，不会停止拔飞虫的翅膀，只会为这种行为想出一个更好的理由。&rdquo;</span></span></div>
<div class="ii gt"><br /></div>
<div class="ii gt"><span class="z19Dle"><span class="zo">4. 《<a href="http://www.amazon.cn/gp/product/B0011BV4IY/ref=oss_product" target="_blank">再造文明的尝试──胡适传</a>》</span></span></div><!--sp--><div class="addfav"><br />收藏到：<span class= "delicious"><a href="http://delicious.com/save?url=http%3A%2F%2Fdear.blogbus.com%2Flogs%2F64791967.html&title=%E4%BA%94%E6%9C%88%E4%B9%A6%E5%8D%95">Del.icio.us</a></span></div><br /><br /><div class="sysmsg"><b><a href="http://www.blogbus.com" target="_blank">博客大巴，你的个人传媒早班车</a></b></div><br /><br />]]></description>
   <link>http://dear.blogbus.com/logs/64791967.html</link>
   <author>dearwolf</author>
   <pubDate>Mon, 31 May 2010 21:43:45 +0800</pubDate>
  </item>
  <item>
   <title>记一个纠结的自动化发布过程（2）</title>
   <description><![CDATA[<p>在上一篇博客中，我用到了AutoIt来自动连接/断开VPN，这固然让我们的发布过程有了自动化的基础，但依然要瞅准时机，在执行测试之前断VPN，执行完测试再连上。这个过程还是手工的、重复的操作。于是下一步要做的就是用脚本把所有操作串起来。</p>
<p>但是该怎么做呢？mvn release这个插件已经把所有步骤封装在一起了，除非先能拆开。但改源码重新发布的成本又太大。然后又想到用管道捕获命令行输出，当程序读到某些字符的时候就调用批处理文件启动AutoIt来操作VPN，貌似可以达成需求。</p>
<p>于是我就稀里哗啦写了些代码。然后贾杨就告诉我他想到更简单的办法了。这个办法的关键是两点：1. 理解maven的<a href="http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html" target="_blank">生命周期</a>。2. <a href="http://maven.apache.org/guides/introduction/introduction-to-profiles.html" target="_blank">理解Profile</a>。</p>
<p>我们在pom.xml写了一个Profile，指定用maven-groovy-plugin（当然也可以用别的，比如mavn-ant-plugin），让它在某个phrase执行断开VPN的脚本，在另一个phrase执行连接VPN的脚本。这个profile默认不启动，在本地的settings.xml里面加这么一条，即告完结：</p>
<blockquote>
&lt;activeProfiles&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;activeProfile&gt;Beijing&lt;/activeProfile&gt;<br />&lt;/activeProfiles&gt;</blockquote>
<p>修改后的pom文件如下：</p>
<p>&lt;profiles&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;profile&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;id&gt;Beijing&lt;/id&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;activation&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;activeByDefault&gt;false&lt;/activeByDefault&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/activation&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;build&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;plugins&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;plugin&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;groupId&gt;org.codehaus.mojo&lt;/groupId&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;artifactId&gt;groovy-maven-plugin&lt;/artifactId&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;version&gt;1.2&lt;/version&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;executions&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;execution&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;id&gt;process-test&lt;/id&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;phase&gt;test-compile&lt;/phase&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;goals&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;goal&gt;execute&lt;/goal&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/goals&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;configuration&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;source&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; println("======================start disconnect");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "dis.bat".execute().text ;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; println("======================end disconnect");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/source&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/configuration&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/execution&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;execution&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;id&gt;prepare-package&lt;/id&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;phase&gt;prepare-package&lt;/phase&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;goals&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;goal&gt;execute&lt;/goal&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/goals&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;configuration&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;source&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; println("======================start connect ");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "connect.bat".execute().text ;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; println("======================end connect&nbsp; ");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep 10000<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; println("======================wait for vpn");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/source&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/configuration&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/execution&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/executions&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/plugin&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/plugins&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/build&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/profile&gt;
<br />&nbsp;&nbsp;&nbsp; &lt;/profiles&gt;</p>
<p>这个事情做完以后，就大功告成了。每每想到日后每一次做release，都可以敲个命令让它自动执行，心情就畅快的很。</p>
<p>回头来看，这个过程的突破点都跟我无关，默默在buzz上推荐了AutoIt，贾杨想到的用profile。有什么难解的地方么？没有。可是项目团队却一直在用这个方式工作。也许是我的忍耐度比较低；也许在我加入项目之前大家都在忙着做其他更急迫的事情，慢慢就习惯了。</p>
<p>我认为忍耐度低是件好事，这表示我在坚持我认为正确的事情，并努力把它做好。</p><!--sp--><div class="addfav"><br />收藏到：<span class= "delicious"><a href="http://delicious.com/save?url=http%3A%2F%2Fdear.blogbus.com%2Flogs%2F64712316.html&title=%E8%AE%B0%E4%B8%80%E4%B8%AA%E7%BA%A0%E7%BB%93%E7%9A%84%E8%87%AA%E5%8A%A8%E5%8C%96%E5%8F%91%E5%B8%83%E8%BF%87%E7%A8%8B%EF%BC%882%EF%BC%89">Del.icio.us</a></span></div><br /><br /><div class="sysmsg"><b><a href="http://www.blogbus.com" target="_blank">博客大巴，你的个人传媒早班车</a></b></div><br /><br />]]></description>
   <link>http://dear.blogbus.com/logs/64712316.html</link>
   <author>dearwolf</author>
   <pubDate>Sun, 30 May 2010 22:27:21 +0800</pubDate>
  </item>
 </channel>
</rss>

