Showing posts with label Tools. Show all posts
Showing posts with label Tools. Show all posts

Monday, January 7, 2008

SVN and proxy servers

So I wanted to check out Jeffrey's sample code he demonstrates in the latest dnrTV episode.  I get a fun message back from SVN, which is completely meaningless to me:

It turns out that although SVN works through port 80, the proxy server I'm behind filters HTTP headers, one of which is PROPFIND.  Google tells me that I can configure SVN to use a proxy server, which is what I need to do with several applications (including Live Writer).  Although I have to hard-code my password into the configuration file, it fixes the problem:

I have to change the configuration file every few months when my password expires, but otherwise I'm good.

Thursday, January 3, 2008

Time expired, still going strong

My Live Writer beta expired at the beginning of the year, but I'm still going strong:

I don't see the "Ask Me.....Never" button, so I'll just click the Later button.  It's still less work to click a button once a day than it is jumping through the hoops to get Live Writer installed on a Server 2003 machine.  I still love the LW, so I'll keep clicking away.

Wednesday, January 2, 2008

Targeting multiple environments through NAnt

One of the nice things about using a command-line local build is that I can easily target multiple environments.  Our configuration scheme is fairly straightforward, with all changes limited to one "web.config" file.

When I refer to multiple environments, I'm talking about many individual isolated deployment targets, such as production, integration, developer, local, etc.  Each environment has its own database, services, maybe even domain.  Sometimes I need to configure my local code to point to different environments, where maybe a defect shows up in production but not our integration environment.

A typical scenario might be that I have different database in each environment.  Different databases means different connection strings, and my connection strings are stored in my "web.config" file.  The problem is that the "web.config" file is stored in source control, and I don't want to always check-in and check-out the file each time I want to target a different environment.

Additionally, I don't want to have to remember the connection string when I switch to a different environment.  I want it all automated, and I want it to just work.

To point our local codebase at different environments, we apply a few tricks to NAnt to make it easy to switch back and forth between many environments.

The command-line build

The first item we have set up is a command-line build and local deployment.  Our environment is a too complex to have only solution compilation to be sufficient to actually run our app, so we use NAnt to build and run our software.  To do this, I have a very simple "go.bat" batch file that calls NAnt with the appropriate command-line arguments:

@tools\nant\NAnt.exe -buildfile:NBehave.build %*

When I call NAnt from the command-line, I can pass in multiple targets without needing to specify the build file or other arguments every time:

go clean test deploy

Now that I can easily call different targets in the build, I can use that mechanism to target different environments, doing something like this:

go PROD clean test deploy

Configuring NAnt

To get a NAnt target to change my configuration, I need a few elements in place:

  • File to hold configuration entries
  • Target to load configuration
  • Tasks to apply configuration

The basic idea is that the "PROD" or "SIT" or "DEV" target will load up specific configuration properties.  After compilation, these configuration properties will be inserted back into the web.config file.  I will have a set of configuration properties for each environment that have the same name, but different values.

Configuration settings file

I like to keep my configuration settings in a separate build file, so I created an "environmentSettings.build" file to hold all of the settings for each environment:

<?xml version="1.0" encoding="utf-8"?>
<project name="Environment Settings" xmlns="http://nant.sf.net/schemas/nant.xsd">

  <target name="config-settings-PROD">
    <property name="connection_string" value="Data Source=prddbsvr;Initial Catalog=AdventureWorks;Integrated Security=true" />
  </target>

  <target name="config-settings-SIT">
    <property name="connection_string" value="Data Source=sitdbsvr;Initial Catalog=AdventureWorks;Integrated Security=true" />
  </target>

  <target name="config-settings-DEV">
    <property name="connection_string" value="Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security=true" />
  </target>

</project>

Two important items to note are:

  • Target names differ only by last part, the target environment
  • Targets all define the same property, namely "connection_string", but these values are different in each example

Selecting configuration

Now that my configuration settings file is finished, it's time to turn our attention back to the main build script file.  I need to add targets to handle "PROD", "SIT", etc.  Additionally, I want to define a property that has a default environment setting.

The targets that handle "PROD", etc. don't need to do much other than re-define the environment setting property and load the targets from the new file.  Here are those targets:

<property name="target-env" value="DEV" />

<target name="DEV">
  <property name="target-env" value="DEV" />
  <call target="load-config-settings" />
</target>

<target name="SIT">
  <property name="target-env" value="SIT" />
  <call target="load-config-settings" />
</target>

<target name="PROD">
  <property name="target-env" value="PROD" />
  <call target="load-config-settings" />
</target>

<target name="load-config-settings" unless="${target::has-executed('load-config-settings')}">
  <include buildfile="${env-settings.file}" />
</target>

The first thing to note here is the declaration of the "target-env" property at the top.  That will be useful later on when making decisions based on the target environment.

Next, I declare a set of targets named after my target environments, namely "DEV", "SIT" and "PROD".  These are also the same names as the postfixes in the target names in my "environmentSettings.build" file I created earlier.  In each of these targets, I override the "target-env" property with its new value, the target environment.  Remember that in my "go.bat" file, all command-line arguments are targets to be executed by NAnt, so I have to create a specific target for each target environment I want to support.

Finally, I call the "load-config-settings" target.  Its responsibility is simply to load the environment settings build file I created earlier, but not to call any of its targets.  The reason for the "unless" part is that NAnt does not allow you to declare the same targets twice, so I need to make sure that the "load-config-settings" target is only executed at most once.

Loading and applying configuration

Now that I have all of the targets loaded, I need to call the appropriate settings target and apply the configuration properties to the web.config file.  This step is usually done post-compilation, but I can apply the settings any time after they are loaded:

<target name="modify-web-config">
  
  <call target="config-settings-${target-env}" />

  <xmlpoke
    file="${deploy.dir}/Web.Config"
    xpath="/configuration/appSettings/add[@key='ConnectionString']/@value"
    value="${connection_string}"
   />

</target>

First, this target calls "config-settings-XXXXX", where the last part is filled in by the "target-env" property declared earlier.  If I chose "SIT", the "config-settings-SIT" target is called.  If I chose "PROD", the "config-settings-PROD" target is called.  Recall also that the "config-settings-XXXX" targets all declare the same properties, but with different values.

Finally, I use the xmlpoke task to modify the web.config file, giving it the new "connection_string" property value set up from the "config-settings-XXXX" target.

Now, if I want to target different environments, all I need to do is put in the environment name when calling the batch script, such as "go SIT deploy-local", and my local app now targets a different environment.  If there are more complex things I need to do based on the target environment, all I need to do is check the "target-env" property.

Wrapping it up

There are many different ways to target different environments, such as web deployment projects and solution configuration.  I found using NAnt integrated well with our command-line build and gave us a maintainable solution, as all build/deployment logic is hosted in one build script, instead of spread over many project or solution configurations.

Tuesday, November 20, 2007

ReSharper in VS 2008

UPDATE 11/26/2007

Jeffrey Palermo notes that you can use R# in VS 2008 by turning off a couple of features.

Ilya Ryzhenkov, product manager at JetBrains, gives detailed information on R# support in VS 2008 and talks about R# 4.0.

After installing VS 2008 Team Suite yesterday, I thought I'd try the ReSharper 3.0 for VS 2008 download.  The verdict?  Works great unless you use any C# 3.0 feature.  Here's one example of it blowing up (but not crashing):

I'm using:

  • var keyword
  • anonymous types
  • LINQ

That was a simple LINQ query to count the number of types in the .NET Framework that implement IDisposable and end in the word "Context" (to settle a bet).  Here's another example:

That's an experimental API for pre- and post-conditions (for pseudo-design-by-contract) that uses:

  • extension methods
  • lambda expressions

From JetBrains' website, they clearly state in the download page:

ReSharper 3.0 for Visual Studio 2008 (Codename "Orcas") only supports C# 2.0 and Visual Basic .NET 8 language features. C# 3.0 will be supported in the next major release of ReSharper.

Now I have a dilemma.  Using Visual Studio without ReSharper is like taking a trip in a horse and buggy instead of a car.  Sure, it'll get you there, but you'll have to deal with a lot of crap along the way.  Until R# v.Next comes out, I'll just have to deal with it.  If you're using VS 2008, don't install R# 3.0 if you plan on using C# 3.0.  Hopefully it'll come out soon, but I haven't seen any roadmaps yet, so I'll just wait.

Monday, November 19, 2007

Installing Visual Studio 2008

As has already been noted in numerous places, VS 2008 dropped today.  The installation process has been surprisingly smooth, given my past installation experiences.

Downloading went surprisingly quick (after I was able to get in to the subscriptions site, which took about 2 hours).  I averaged around 900KB/sec (that's bytes, not bits), about 10 times more than I expected on RTM day.

Cleaning up

First, you'll need to make sure that you don't have any Beta bits installed.  I had VS 2008 Beta 2 on my dev machine, and no problems cropped up during uninstallation.  I removed pretty much anything that had "2008" and "3.5" in its name, as I had installed quite a few of the Beta 2 releases.

Additionally, I removed them in the reverse order I installed them.  Don't uninstall VS 2008 before Team Explorer for example.  My lack of later installation issues might be attributed to this.

Installing

Even after I uninstalled the 2008 and 3.5 apps I found, the 2008 installer noted that I had some pre-release software still installed (.NET Compact Framework) that needed to be removed.  Their installers have always been good about letting you know about conflicting versions, but it's a relief that it plays well with Beta 2.

About halfway through the installation, the installer noted that I needed to close some apps.  These included IE and XML Notepad.  This always happens with me and various MS installers, but I primarily use Firefox, so I didn't have to close any windows I needed.

About an hour later, all of the installations finished (VS 2008 Team Suite, Team Explorer 2008, and MSDN).

First impressions

After the obligatory first-launch screen (which settings do you want?  Whatever ReSharper does!), I noticed that the "Recent Projects" list included the same set of projects as Beta 2, and they all opened with no issues.

The only problem so far is that there is no officially supported version of ReSharper for VS 2008 RTM, and the website says that the next major release will be the first officially supported version.  But let's be honest, any time spent without ReSharper just makes us love it even more, right?

Wednesday, November 7, 2007

Some NAnt tips

Jason got me thinking on some other NAnt tips from his post on listing the targets in the build file.

Include NAnt in your source control

I like to put NAnt in source control and NEVER install it.  I've had too many problems upgrading NAnt and having projects break because of whatever reason, and I have even more issues with custom tasks I write.  It helps to keep your dev machine clean, your build machine clean, and leads to faster dev machine setup time.  Here's my current favorite source tree structure:

  • <ProjectName/Version/Branch>
    • src
      • Where source code goes
    • lib
      • any Dlls referenced from projects, including NUnit dll's
    • tools
      • NAnt
      • NUnit
      • NCover
      • any other exe needed for build/development
    • docs
    • project.sln
    • project.build
    • go.bat

This was all inspired by (stolen from) TreeSurgeon.

Put your build file in your solution

I like to create a solution folder to hold extra project-related files, like my build scripts.  When they're in the solution, it makes it easier to load them up and edit them.  Since ReSharper supports many features for NAnt and MSBuild scripts, it makes sense to have them easy to get to.

I already talked about this earlier, but solution folders are a great way to create logical organization in your solution that doesn't have to correspond to the physical organization on disk.

Create a NAnt bootstrap batch file

I can't stand typing in "nant -buildfile:blahblahblah", especially when I execute the NAnt version that's in my source tree (not the one installed).  I like to create a one-line batch file called "go.bat" that bootstraps NAnt and my build file:

@tools\nant\NAnt.exe -buildfile:NBehave.build %*

The @ symbol tells cmd.exe not to echo the command text out, then I specify the location of NAnt and the build file.  The last part, "%*", just tells cmd.exe to append all commands after "go.bat" to the "nant.exe" program.  For example, I can now do this:

go clean test deploy

The "clean test deploy" commands are targets in my NAnt build script.  Any other NAnt command line options can be typed in after "go".  I put this batch file at the project root level.

Use the "description" attribute to document public targets

As Jason mentioned, the "-projecthelp" NAnt switch will output a list of targets in your NAnt script.  By providing a description in your target, you can provide some better information in the help output.  Here's what NBehave's help looks like:

The targets are separated into Main and Sub Targets.  Main Targets are targets with descriptions, and Sub Targets are those without.  I can still provide internal documentation with XML comments in the build script.  By only providing descriptions on targets that I need to call often, I can create some level of encapsulation in my build file.

I'm sure there are a dozen better tips out there, but these are the ones I make sure to use on every project I'm involved with.

Friday, October 19, 2007

Smart Tag shortcut key

Ever notice that little red bar show up sometimes in while coding in Visual Studio 2005?  It shows up after making certain changes to code, such as renaming methods, fields, or types that need to be imported:

That little red bar (did you miss it?  Look closer!) brings up a helpful context menu that has some nice refactorings/editing commands.  Unfortunately, to get the menu to show up with your mouse, you have to bring the mouse down from above onto that little bar that's about 10 pixels wide.

That's extremely annoying, and I've often missed many times trying to hit that little bar.  Couple that with the fact I don't like to use the mouse while coding, and it's even more annoying.

Luckily, VS 2005 has a nice shortcut combination to bring that menu up: Ctrl+.  After that combo, the menu shows up:

Now you can just hit "Enter" and it performs the highlighted command.  For ReSharper users, "Ctrl+." is to Visual Studio as "Alt+Enter" is to ReSharper.  I'm probably the last person to know this shortcut, but it's nice for the few times I prefer Microsoft's helper menu over ReSharper's.

Wednesday, October 10, 2007

Subscribing to your Google Calendar in Outlook 2007

I've been in love with Google Calendar for a long time now.  I have several calendars I'm viewing at the same time, including my personal calendar, UT football schedule, and a few others.  I always hated having to view two calendars at work, one for my work schedule and one for my Google calendars.

Scott Hanselman mentioned that he views multiple external calendars inside Outlook 2007.  Several of the external calendars are Google calendars.  Outlook 2007 supports any ICAL source, which most online calendar applications provide.  It's easy to set up, and the instructions can be found here:

Subscribe to your Google Calendar 

Making your Outlook calendar show up in Google calendar is a bit more tricky, and I haven't found anything that does actual synchronization and not just import/export.

Now I have all of my calendars in one convenient location, with each calendar color coded from their source.  However, I found I have to think of more creative excuses on why I forget things my wife's calendar...

Monday, October 1, 2007

Setting off a CC.NET build from NAnt

One of the new features in CruiseControl.NET are integration queues with priorities.  By default, all projects can execute concurrently, but sometimes, I want to have a master build execute first, then dependent builds next.  This is fairly easy to do, using the integration queues, priorities, and force build publishers.

However, I have a strange situation where I actually need to force a build from a NAnt script (don't ask).  There aren't any command-line tools to execute CCNET builds, so I couldn't use the <exec> task to accomplish this.  Instead, I'll create a custom NAnt task.  To do this, I'll need to create a new C# project and reference three key assemblies:

  • NAnt.Core.dll
  • ThoughtWorks.CruiseControl.Core.dll
  • ThoughtWorks.CruiseControl.Remote.dll

From there, it's just a matter of creating a custom task to call into the CCNET API:

[TaskName("ccnet")]
public class CCNet : Task
{
    private string _server;
    private int _portNumber = 21234;
    private string _project;

    [TaskAttribute("server", Required = true)]
    public string Server
    {
        get { return _server; }
        set { _server = value; }
    }

    [TaskAttribute("portnumber", Required = false)]
    public int PortNumber
    {
        get { return _portNumber; }
        set { _portNumber = value; }
    }

    [TaskAttribute("project", Required = true)]
    public string ProjectName
    {
        get { return _project; }
        set { _project = value; }
    }

    protected override void ExecuteTask()
    {
        RemoteCruiseManagerFactory factory = new RemoteCruiseManagerFactory();
        string url = string.Format("tcp://{0}:{1}/CruiseManager.rem", Server, PortNumber);
        ICruiseManager mgr = factory.GetCruiseManager(url);

        string proj = mgr.GetProject(ProjectName);

        mgr.ForceBuild(ProjectName);
    }
}

The custom NAnt task needs at least two pieces of information to connect to the CCNET server:

  • Server name
  • Project name

If the remoting port is different than the default (21234), I'll need to specify that as well.  Now I can execute a CCNET build with this simple NAnt task:

<ccnet server="buildserver" project="Ecommerce CI" />

I'd still rather use the integration queues, priorities, and publishers, but the NAnt task will work for me in a more complex scenario.

Wednesday, September 26, 2007

Organizing with Solution Folders

I don't know how long it's been around, but I found a nice organization feature for VS Solutions, Solution Folders.  Solution Folders are logical (not physical) folders that you can use to organize projects and solution items.  You can nest folders as deep as you like also, just right-click the solution in the Solution Explorer and click "Add->New Solution Folder".

It's easy to get carried away, but I like to organize my projects by app, when I have over 10 or so projects in my solution.  Typically, it will look like this:

  • Common Projects
    • Tests
  • Windows App
    • Tests
  • Web App
    • Tests
  • Build

Here's an example with NBehave:

This solution was small enough that it really didn't need it, but with a solution with several dozen projects and many applications, solution folders can prevent quite a few headaches.

Friday, September 21, 2007

New favorite deployment method

Having run into so many crazy errors from installing MSI's, I've settled on a new favorite deployment mechanism: NAnt (or MSBuild).  Here's how it works:

  • Package application and build scripts into one ZIP file
  • Package any tools needed for the build (NAnt, NUnit, etc.)
  • Package a bootstrapper batch file that calls NAnt and your build file with the appropriate targets
  • Run the batch file

Pros

  • Install script can be the same script as run on a local dev box
  • Can package NAnt along with application, so no need to install any tools on their machine
  • More robust than XCopy deployment
  • Far fewer headaches than with MSI deployment
  • No need for proprietary scripting languages to do complex work, instead use open-source, standard tasks
    • No more Wise script or InstallScript

Cons

  • Not for commercial products
  • MSI's do take care of a lot of boilerplate tasks
  • MSI's have rollback and uninstall built in
    • Though with custom actions, still a pain
  • Nice UI
  • Familiar interface

Here's what my final ZIP file looks like:

  • Product-Dist.zip
    • Product.zip (zipped up application)
    • Tools
      • NAnt
      • NUnit
    • install.build
    • Install.bat
    • Uninstall.bat
    • Go.bat

What's really cool is that the structure of the distribution zip file matches my local structure, so installing locally is the exact same as installing on a clean box.  Also, I can deploy NUnit and the tests, so I can run tests on deployed machines for some extra build verification.

Tuesday, July 24, 2007

If you can't wait for Team Build 2008...

I just heard of a new project on CodePlex, TfsBuildLab.  It looks like it performs a lot of the build features Orcas will provide, and it lets you do it with Team Build 2005:

  • Continuous integration builds
  • Scheduled builds
  • Build queuing
  • Build cleanup/retention policies
  • Event notifications

The TfsBuildLab project is broken into 4 separate components:

  • Database
    • Houses the scheduling and queuing data
  • Service
    • Queues builds
    • Executes manual builds
    • Executes scheduled builds
  • Admin client
    • Configures build schedules, etc.
  • Notification client
    • Notifies clients of build status, similar to CCTray

Builds are still executed by Team Build, but TfsBuildLab puts a layer on top of Team Build to provide the extra functionality.  This looks like a nice interim solution until Team Build 2008 releases early next year.

Thursday, May 3, 2007

Another reason to love ReSharper

Whenever I look at a class in code for the first time, I try to figure out what its responsibilities are.  Ideally, I would look at unit tests exercising the public interface.  These unit tests will give me a description of functionality, code showing usage, and most likely some assertions describing and verifying behavior.  If there aren't any unit tests, I have to resort to figuring out what the code is doing by examining the code (which is about 10 times slower than looking at unit tests).

First off, I'll look at the using directives at the top of the file.  Using directives can provide a key insight into the behaviors and responsibilities of a class, since the using directives show me what the dependencies are.  For highly cohesive, loosely coupled code, you probably won't see a ton of using directives, as a general rule of thumb.  I was a little disheartened when I saw these using directives:

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Web;
using System.Web.Services;
using System.DirectoryServices;
using System.Data.SqlClient;
using System.Drawing;
using System.Drawing.Design;
using System.ComponentModel.Design;
using System.Configuration;
using System.Xml.Serialization;
using System.Xml;
using System.IO;

using GL = System.Globalization;
using CMP = System.IO.Compression;
using MSG = System.Messaging;

using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Security;
using MyCompany.Commerce.BusinessObjects.Shipping;
using MyCompany.Commerce.Core;
using SCF = MyCompany.Commerce.StoreConfiguration;
using MyCompany.Commerce.Core.BusinessObjectManager;
using SH = MyCompany.Commerce.BusinessObjects.Ecomm.Security.SecurityHelper;
using ctx = MyCompany.Commerce.BusinessObjects.StoreContext;
using MyCompany.Sales.Security;
using MyCompany.Sales.Profile;
using Microsoft.ApplicationBlocks.ExceptionManagement;

Wow.  That's a lot of using directives.  Boooo.  It looks like this class cares about web services, ADO.NET, XML serialization, IO, session state, and the list goes on and on.  Waaaay too much for one class to care about.  If there were a such thing as code referees, this class would get red carded for a flagrant foul of the Single Responsibility Principle.  Luckily, I have Resharper.

ReSharper to the rescue

ReSharper is pretty much crack for developers.  Once you use it for a week or even a day, you'll find it nearly impossible to develop without it.  So how did ReSharper help me in this situation?  With a nifty "Optimize using directives" refactoring of course.  The "Optimize using directives" refactoring will remove unused using directives, and can also add new ones where you've qualified type names with namespaces.  Here are the using directives after the refactoring:

using System;
using System.IO;
using System.Web.Services;
using System.Xml.Serialization;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using BO = MyCompany.Commerce.BusinessObjects;
using Microsoft.ApplicationBlocks.ExceptionManagement;
using MyCompany.Commerce.BusinessObjects.Shipping;
using MyCompany.Commerce.Core;
using SCF = MyCompany.Commerce.StoreConfiguration;
using MyCompany.Commerce.Core.BusinessObjectManager;
using SH = MyCompany.Commerce.BusinessObjects.Ecomm.Security.SecurityHelper;
using ctx = MyCompany.Commerce.BusinessObjects.StoreContext;
using MyCompany.Sales.Security;
using MyCompany.Sales.Profile;

HUGE improvement in readability.  Now I can be fairly certain what the external dependencies of this class are, as we reduced the number of using directives from 34 (!) to 18, around half.  There's still way too many dependencies, I'd like to see this number reduced to around 5 or so.  ReSharper will also sort the using statements to group common directives together (it doesn't work for using aliases).  Now it's not as big a leap as it was before to try and figure out what this class does.  That's one less ibuprofen I'll need today :).  Of course, if this class had been properly unit tested, I wouldn't need the ibuprofen in the first place, but that's another story.