Update version number in vbproj file in NAnt build

Today, I got an exact same challenge Scot Hanselman faced 8 years ago. 

Copied his script into my build file, error said: Regex does not exist.

Added system.dll reference in NAnt according to this post, no help.

I stopped, why bother using Regex for such a small task.

I turn back to native NAnt tasks. Strategy is,

  1. Find the hardcoded version number in vdproj file, e.g., 1.1.0, notify team member don’t touch it, this will be my constant, or token to be replaced later.
  2. Replace this 1.1.0 on the fly during the build, build msi. Create backup vdproj file before replacing.
  3. When build finished, restore original vdproj.

Simple and clean solution, job accomplished.

Only goofy part is, if I run this build script when Visual Studio opened, VS kept saving memory cached content to disk, causing file conflict.

Not a big deal, this build target only runs on our CI server anyway.


  <property name="orig.version.number.in.vdproj" value="1.1.0" /> <!--ensure this matches vdproj-->
  <property name="path.vdproj.file" value="src\EmailProcessorNTServiceSetup\EmailProcessorNTServiceSetup.vdproj" />
  <property name="path.vdproj.file.orig" value="${path.vdproj.file}.orig" />

  <target name="update.vdproj.version" depends="">
    <loadfile file="${path.vdproj.file}" property="file.contents" />
	<!-- build.number in format of xx.xx.xx.xx, need to shorten to xx.xx.xx to satisfy vdproj -->
    <property name="version.number" value="${string::substring(build.number, 0, string::last-index-of(build.number, '.'))}"/>
	<echo message="shorteded version.number: ${version.number}" />
    <echo file="${path.vdproj.file}" message="${string::replace(file.contents, orig.version.number.in.vdproj, version.number)}" />
  </target>  
 
  </target>

  <target name="restore.vdproj.file" depends="">
    <delete file="${path.vdproj.file}" />
	<echo message="${path.vdproj.file} could not be deleted" if="${file::exists(path.vdproj.file)}" />

	<if test="${not file::exists(path.vdproj.file)}">
		<copy file="${path.vdproj.file.orig}"
          tofile="${path.vdproj.file}"

          />
		<delete file="${path.vdproj.file.orig}" />
	</if>
<!-- can also replace it back, same result, but this potentially still mess up svn flag.    <echo file="${path.vdproj.file}" message="${string::replace(file.contents, version.number, orig.version.number.in.vdproj)}" />-->
  </target>
Advertisements

Add modificationHistroy to release?

I wish we could use JIRA for generating release file in build result. cc.net does have a ModificationHistory publisher, but for some reason, the version CCConfig I have doesn’t work for this. (Somebody filed the bug already, I did a vote.)

The workaround I’m using:

  1. Enable ModificationWriterTask in cc.net project publisher, this will generate a file named modifications.xml in artifact folder, which only contains the changes since last build.
  2. Create a NAnt task to merge modifications info into a modificationHistory.xml file. (idea from here)
  3. Create another NAnt task to transform the xml into a text file.
	<target name="publish.mod.history" depends="merge.mod.history" >
		<style style="${build.dir}\modifications.xsl"
			in="${CCNetArtifactDirectory}\modificationHistory.xml"
			out="${CCNetArtifactDirectory}\${application.version}\modifications.txt">
	    <parameters>
	        <parameter name="reportType" namespaceuri="" value="Plain" />
	    </parameters>
		</style>
	</target>

	<target name="merge.mod.history" description="add the new mod list to an existing xml file">
		<property name="xmlnodes" value=""/>
		<xmlpeek xpath="//ArrayOfModification" file="${CCNetArtifactDirectory}\modificationHistory.xml" property="xmlnodes"></xmlpeek>
		<property name="newnode" value="" />
		<xmlpeek xpath="//ArrayOfModification" file="${CCNetArtifactDirectory}\modifications.xml" property="newnode"></xmlpeek>
		<property name="xmlnodes" value="${newnode}${xmlnodes}" />
		<xmlpoke file="${CCNetArtifactDirectory}\modificationHistory.xml" xpath="//ArrayOfModification" value="${xmlnodes}" />
	</target>

Modifications.xsl:

<?xml version="1.0"?><!-- DWXMLSource="modificationHistory0.xml" -->
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"

     version="1.0">

    <xsl:output method="text"/>

    <xsl:variable name="modification.list" select="ArrayOfModification/Modification"/>

    <xsl:template match="/">
Modifications since last build (<xsl:value-of select="count($modification.list)"/>)
    <xsl:apply-templates select="$modification.list">
        <xsl:sort select="date" order="descending" data-type="text" />
    </xsl:apply-templates>
    </xsl:template>

    <!-- Modifications template -->
    <xsl:template match="Modification">
 (Revision:<xsl:value-of select="ChangeNumber"/>) <xsl:value-of select="FolderName"/>/<xsl:value-of select="FileName"/>, <xsl:value-of select="UserName"/> on <xsl:value-of select='substring(ModifiedTime, 0, 11)'/>, <xsl:value-of select="Comment"/>
    </xsl:template>

</xsl:stylesheet>

Result:

Modifications since last build (14)

(Revision:15) /trunk/build/modificationHistory0.xml, FMao on 2009-10-15,
(Revision:15) /trunk/build/modifications.xsl, FMao on 2009-10-15,
(Revision:12) /trunk/build/default.build, FMao on 2009-10-15, added new task to merge mod hist and transform xml
(Revision:12) /trunk/pbunit_demo.pbw, FMao on 2009-10-15, added new task to merge mod hist and transform xml

Replace file content in NAnt task

As JP suggested, I always compile all my layered projects into a huge dll to make NAnt build file eaiser. This solution is perfect until I tried to embeb structuremap config file into cruise control. Because assembly file name changed, I have to find a way to replace the config file content in NAnt.

It was not easy. NAnt doesn’t have a replace task!

I don’t want hack into NAnt source to re-build my own version. It seems Jay Flowers, the creator of CI Factory has his modified version of NAnt which has replace task in it, but, no document to direct me how to use it.

OK, I have to call Ant task in NAnt build file then.


[NAnt build file]
<exec program="c:\ant\bin\ant.bat" basedir="${base.dir} ">
<arg value="-Dcurrentproject.lib=${currentproject.lib}" />
</exec>

    [Ant file]
    <project name="config" default="replace_config" basedir=".">

    <property name="build.dir" value="${basedir}\build"/>
    <property name="currentproject.lib" value="undefined"/>

    <target name="replace_config">
    <replace file="${build.dir}\StructureMap.config">
    <replacefilter token=",mybizlayer" value=",${currentproject.lib}"  />
    <replacefilter token=",mydatalayer" value=",${currentproject.lib}"  />

    </replace>

    </target>
    </project>

Strange NAnt problem

When I moved my dotnet project from my workstation to CC.net server (64 bit windows 2003 server), this problem appears:

System.NullReferenceException: Object reference not set to an instance of an object.
at NAnt.Core.FrameworkInfo.get_Version()

There is a solution on Jeffery Palermo’s blog in which a extra dotnet 3.5 configuration is provided. I didn’t want to do this at first because the version I have (.86 beta1) already include this part. I decided to change my xml file little by little with no good, finally, I copied the whole piece of  Jefferyof xml about .net 35 config to NAnt.exe.config, the problem was fixed then.

There must be something different in the assembly list, for 64 bit server maybe?