Foundational Software Development Practices

Foundations of Software Development

  1. Version Control

    For any repeatable process version control is essential. Here are some notes on using subversion in a windows XP environment with the svnserver. I'd recommend downloading the TortoiseSVN client and the ankhsvn Visual Studio integration plugin. Get a free book.

    1. Getting Started
      1. Download and Install the software

        Visit https://subversion.tigris.org/. Follow instructions. Takes about 2 minutes for the entire process.

      2. Create a repository
        C:\>mkdir c:\svn-repos
        C:\>svnadmin create c:\svn-repos
        
      3. Start the service

        This is only needed if you are going to use the svn protocol to talk with the repository. This is best if everyone is using a private network. You can also use http, a local file URL, or svn+ssh.

        start svnserve --daemon --root c:\svn-repos
        
      4. Configure the svnservice

        Go to C:\svn-repos\conf and edit the following files:

        1. svnserve.conf
          [general]
          anon-access = read
          auth-access = write
          password-db = passwd
          
        2. passwd
          [users]
          whedon = secretpassword
          mreynolds = serenity
          zwashbourne = browncoats
          
      5. Import source files into repository

        Go to the top level of a temporary directory that contains your source code and import it into a project name/trunk. In the following example, "mbus-pd1317" is the machine name hosting the svnservice and "scythe" is the project name. Be careful, subversion cares about case even on windows.

        svn import -m "initial import" . svn://mbus-pd1317/scythe/trunk
        

        It will ask you for a password and then import your files into subversion.

      6. Get a copy into your working directory

        Now that the code is in the repository, change directory to one level above where you want the files and checkout the files. (Note: the checkout does not lock the files.)

        cd C:\workfiles
        svn co svn://mbus-pd1317/scythe/trunk scythe
        
      7. Delete extra files

        If you're like me, you included extra files. To remove the extra directories of "bin" and "obj", navigate to the directory above them and use the "del" command.

        svn del bin obj
        
    2. Normal Operations
      1. setting the editor

        subversion will want to use 'vi' as the default editor for entering comments on operations. You can use notepad instead by entering the following:

        set SVN_EDITOR=notepad
        

        (To make permanent: right-click on "My Computer"/Properties/Advanced.../Environmental Variables and set a value for SVN_EDITOR).

        You can also use vi the SFU package from microsoft and set the terminal type to 'interix' (set TERM=interix).

      2. update

        Before editing a file, it's a good idea to get a fresh copy from the repository with the update command.

        svn update hello.rb
        At revision 114.
        
      3. Checking in a file

        You don't have to "check out" a file and get a lock on it, although you can. Although most people are queasy about this, it's a good thing - really. To check in a file,

        svn ci hello.rb -m"got dependency injection working."
        Sending        hello.rb
        Transmitting file data .
        Committed revision 115.

      What happens if someone else has checked in the file since you got your copy?

      C:\home\mfincher\ruby>svn ci -m"add ! on first line."
      Sending        hello.rb
      svn: Commit failed (details follow):
      svn: Out of date: '/ruby/trunk/hello.rb' in transaction '49-1'
      

      You have to do an update

      C:\home\mfincher\ruby>svn update
      G    hello.rb
      Updated to revision 49.
      

      The 'G' tells us subversion was able to merGe changes successfully.

      What if you have changed the same lines as the new version and subversion can't automatically merge?

      C:\home\mfincher\ruby2>svn update
      C    hello.rb
      Updated to revision 50.
      

      The 'C' means a Conflict occured.

      If you look at your file, conflict markers have been added.

      C:\home\mfincher\ruby2>cat hello.rb
      <<<<<<< .mine
      puts "Helo World?"
      =======
      puts "Helo World!!"
      >>>>>>> .r50
      puts "goodbye!"
      

      Hand edit your code to resolve the conflict and the tell subversion you have resolved the issue.

      C:\home\mfincher\ruby2>svn resolved hello.rb
      Resolved conflicted state of 'hello.rb'
      

      Then checkin your resolved code.

      C:\home\mfincher\ruby2>svn ci -m"changed puncuation to ?!" hello.rb
      Sending        hello.rb
      Transmitting file data .
      Committed revision 51.
      
    3. Add a workfile

      use the 'add' command. Note that it is note in the repository until you do an update.

      C:\home\mfincher\ruby>svn add hello.rb
      A         hello.rb
      
      C:\home\mfincher\ruby>svn ci -m"initial version" hello.rb
      Adding         hello.rb
      Transmitting file data .
      Committed revision 41.
      
    4. Viewing information
      1. Viewing the repository

        Using tortoiseSVN we can look at our repository. Right click on any directory.

        SubversionTortoiseMenu.jpg

        and select the repository browser. Enter the location of your repository in the URL text field. Then you can navigate around the database.

        SubversionTortoiseBrowser.jpg
      2. status command

        Normally "status" tells you which files are not under version control. With the "--verbose" or "-v" option it shows the version number of files.

        svn status -v
        

        To see if your copies are out-of-date with the repository's (-u is the abbreviated form):

        svn status --show-updates
        
      3. svn diff

        Shows all the differences in your files and the repository.

        svn diff
        
      4. svn info

        Shows info about the repository attached to your directory.

        svn info
        
      5. svn list

        Shows the files in the repository, regardless of what is in your local directory.

        svn list
        
      6. svn cat

        types the contents of the revision to the screen

        svn cat hello.rb -r49
        
    5. revisions

      subversion has global revision numbers, not for individual files. To help identify versions, four psuedo-version keywords are available: HEAD - the latest copy in the repository; BASE - the unmodified local copy; COMMITTED The last revision where an item changed before BASE; PREV - the one before the last change. Dates can also be used to find versions. Dates, keywords and revision numbers can be used freely together.

      svn log -r HEAD    #get the latest checkin comments
      svn diff -r 103:113 hello.rb
      svn log -r 103:113 hello.rb
      svn diff --revision {2006-05-22}:COMMITTED hello.rb
      
    6. revert command

      If you have only changed your local copy of a file, you can use the "revert" command to get a fresh copy of it from the cache.

      svn revert test.cs
      
    7. Reverting to an earlier version.

      You can revert a single file or the whole project with the merge command. The following takes the repository back from version 55 to version 53.

      svn merge -r 55:53 .
      
    8. merge command

      The merge command can be used to take your repository back in time. For example, if you decide that all the changes since 56 are bogus, you can revert your entire tree back to version 53 with the following:

      svn merge -r 56:53 svn://mbus-pd1317/scythe/trunk
      svn commit -m"removed all the margarita inspired code and went back to 53"
      
    9. copy

      copy can be used to bring back the dead. If you want to use an older version of a file, copy the revision you need into your tree and then commit.

      svn copy --revision 53 svn://mbus-pd1317/scythe/trunk/file1.c ./file1.c
      
    10. svn co file:///c:/svn-repos/ruby/trunk -r 50 .
      
    11. Tags and Branching
      1. Creating tags

        Tags in subversion are really just directories. To start, create a "tags" subdirectory.

        svn mkdir -m "tags directory" svn://mbus-pd1317/scythe/tags
        

        Then copy the trunk over to the tags directory

        svn copy -m "v0.81" svn://mbus-pd1317/scythe/trunk svn://mbus-pd1317/scythe/tags/v0.81
        

        subversion doesn't really copy a physical version, just a logical one.

      2. Retrieving tagged files.

        To get a particular tagged version, checkout the directory with the tag.

        svn co svn://mbus-pd1317/scythe/tags/v0.81 .
        
    12. Misc commands
      1. svn cleanup

        Removes leftover flotsam

        svn cleanup
        
      2. Backup the repository

        Backing up a repository is easy.

        cd c:\svn-repos
        C:\svn-repos>svnadmin dump . > svn-dump-2006-06-08
        * Dumped revision 0.
        * Dumped revision 1.
        ...
        
      3. Load a repository

        C:\>svnadmin create svn-reposNew
        C:\>svnadmin load svn-reposNew < svn-repos\svn-dump-2006-06-08
        
      4. help

        'help' provides concise summaries of the commands.

        svn help
        svn help merge
        
  2. Build - Nant (Real Section Coming Soon)
    1. Miscellanous tips
      1. Using target::get-current-target() to avoid repeating yourself (DRY)

        When one nant files calls multiple others with the same target, it's handy to use the "get-current-target" function to avoid duplicating the target name - this may get you into trouble when cutting and pasting.

        <target name="clean" depends="init" failOnError="false">
            <nant buildfile="ScytheComponents.build" target="${target::get-current-target()}" />
            <nant buildfile="ScytheService.build" target="${target::get-current-target()}" />
          </target>
        
      2. How to replace text in a string variable

        We don't need the "${}" around the "build.version".

          <property name="build.version" value="3.4" />
          <property name="db.suffix" value="${string::replace(build.version,'.','')}" />
          <echo message="db.suffix:  ${db.suffix}"/>
        
      3. How to pass in variables that override internal properties

        Here's how to set the location of the nunit console app from the command line. If not set on the command line, a default is used.

        nant -buildfile:SurveyDirectorCc.build -D:nunit.console.exe="C:/Program Files/NUnit 2.4.1/bin/nunit-console.exe"
        
         <property name="nunit.console.exe" value="E:\Nibus\Apps\NUnit 2.4.1\bin\nunit-console.exe" unless="${property::exists('nunit.console.exe')}"/>
        
    2. Working with Visual Studio 2008
  3. Testing - NUnit (Coming Soon)
  4. Logging - Log4Net (Coming Soon)
  5. CruiseControl.Net

    Cruise Control aids in continuous integration. CC.Net will kick off a build process when the events you specify fire. Typically this is a checkin of new code. CC.Net will monitor your source control system at a frequency you select.

    1. Download

      CruiseControl.Net can be downloaded from sourceforge.net

    2. Edit the ccnet.config

      Located at C:\Program Files\CruiseControl.NET\server\ccnet.config. Here's an example:

      <cruisecontrol>
      	<project name="Scythe">
      		<triggers>
      			<intervalTrigger seconds="600" buildCondition="IfModificationExists"/>
      		</triggers>
         <!-- CruiseControl needs this to know when something has been checked in -->
      		<sourcecontrol type="svn">
      			<trunkUrl>svn://myversioncontrolserver/scythe/trunk</trunkUrl>
      			<workingDirectory>c:\dev\scythe</workingDirectory>
      			<username>myusername</username>
      			<password>mypassword</password>
      			<tagOnSuccess>false</tagOnSuccess>
      		</sourcecontrol>
      		<tasks>
      
      			<nant>
      				<executable>C:\opt\nant-0.85\bin\nant.exe</executable>
      				<baseDirectory>C:\workfiles\scythe\InProgress\scythe-build\v0.85-debug</baseDirectory>
      				<buildArgs></buildArgs>
      				<buildFile>C:\workfiles\scythe\Scythe_Nightly.xml</buildFile>
      				<targetList>
      					<target>stop</target>
      					<target>uninstall</target>
      					<target>clean</target>
      					<target>get</target>
      					<target>build</target>
      					<target>package</target>
      					<target>deploy</target>
      					<target>report</target>
      					<target>install</target>
      					<target>start</target>
      				</targetList>
      				<buildTimeoutSeconds>1800</buildTimeoutSeconds>
      			</nant>
      			<nunit>
      				<path>C:\Program Files\NUnit 2.2\bin\nunit-console.exe</path>
      				<assemblies>
      					<assembly>C:\workfiles\scythe\InProgress\scythe-build\v0.85-debug\src\ScytheComponents\bin\debug\ScytheComponents.exe</assembly>
      				</assemblies>
      			</nunit>
      		</tasks>
      	</project>
      </cruisecontrol>
      
    3. CC can be run in two different modes.

      To start with it's best to run in the console so you can debug your scripts.

      C:\home\mfincher>cd C:\Program Files\CruiseControl.NET\server
      C:\Program Files\CruiseControl.NET\server>ccnet.exe
      
    4. buildCondition

      For testing swith the buildCondition from "IfModificationExists" to "ForceBuild".

      <triggers>
      	<intervalTrigger seconds="10" buildCondition="ForceBuild"/>
      </triggers>
      
    5. Using Perforce as source control

      Note in the sample code block below that the "client" element is really a "workspace" in perforce.

          <sourcecontrol type="p4">
            <view>//depot/scythe/</view>
            <executable>c:\Program Files\Perforce\p4.exe</executable>
            <client>scythe-build</client>
            <user>fincherm</user>
            <port>myhost:1666</port>
            <applyLabel>true</applyLabel>
            <autoGetSource>true</autoGetSource>
            <forceSync>true</forceSync>
            <p4WebURLFormat>https://localhost:8080/@md=d&amp;cd=//&amp;c=3IB@/{0}?ac=10</p4WebURLFormat>
          </sourcecontrol>
      

      The new workspace "scythe-build" has a view of "//depot/scythe/... //scythe-build/..." and a root of "C:\depot\scythe". The root has to go straight to your branch.

    6. How to delete all the files before your ccnet run?

      Use the "prebuild" tag. You cannot use the "<del dir="mydir" />" since CCNET doesn't understand regular nant tags. So you have to use the "exec" tag. Since "del" is not a regular executable, but it hides in the "command" program you have to do a little more work:

      <prebuild>
            <exec>
              <baseDirectory>c:\workfiles\scythe</baseDirectory>
              <executable>C:\WINDOWS\system32\cmd</executable>
              <buildArgs>/C del/F/Q/S c:\workfiles\scythe</buildArgs>
              <buildTimeoutSeconds>10</buildTimeoutSeconds>
            </exec>
          </prebuild>
      
    7. Web site

      CruiseControl will create a web site for you to view and manage the building process

      The url is: https://buildhost/ccnet