Peter Miller

in

July 2009 - Posts

CI and Configurable Service Installers

Continuous Integration

Continuous Integration (CI) is the practice of continually incorporating code changes into an automated build to ensure that breaking changes are detected as quickly as possible. CI is prevalent among open source projects in general and in the non-.NET ecosystems of Java, Python and Ruby. In my .NET work, the majority of projects have not used CI. Build was always a necessary evil, an annoyance to be avoided and sometimes even the entirety of a persons’ work for a given iteration.

After a lot of reading, but little practical experience with it, I came to view CI as a necessity on my current project. For this project, there are a number of separate components that intertwine to form the full system. This system needs to be demoable in short order, so it was too risky to have build and deploy be an ad-hoc affair.

An apparent disadvantage to CI is that there is a big investment upfront. I had code that compiled just fine inside of Visual Studio and a drag and drop style of deployment that basically worked. I still have a lot of effort to go from there to automated build and deploy.

However, there are tangible benefits to the process itself. Automating build and deploy forced me to really understand how the system I am working on fits together. It also revealed the extent of interdependencies in the code base.

 

Configurable Service Installers

Fighting with TFS and Team Build also lead me to this tip on how to make the installers for Windows service projects configurable from the command line. Windows services can be installed using installutil, which can take command line arguments.

These command line arguments are passed into the Installer class as the Context.Parameters. I overrode the OnBeforeInstall, OnBeforeUninstall and OnBeforeRollback methods to look at these context parameters and set the service name, username and password based on the command line arguments.

This allows me to easily have multiple instances of the same service running on the same machine without recompiling the service project for each new name.

So I end up being able to do something like this from the command line:

> installutil.exe /serviceName=”MyService” /username=serviceUser /password=servicePw serviceExe

And then I have code in my service installer class like this:

        protected override void OnBeforeInstall(IDictionary savedState)
        {
            base.OnBeforeInstall(savedState);
            ReadContextParameters();
           
        }

        protected override void OnBeforeUninstall(IDictionary savedState)
        {
            base.OnBeforeUninstall(savedState);
            ReadContextParameters();

        }

        protected override void OnBeforeRollback(IDictionary savedState)
        {
            base.OnBeforeRollback(savedState);
            ReadContextParameters();
        }

        private void ReadContextParameters()
        {
            if (Context.Parameters.ContainsKey("serviceName"))
            {
                string serviceName = Context.Parameters["serviceName"];
                serviceInstaller1.DisplayName = serviceName;
                serviceInstaller1.ServiceName = serviceName;
            }
            if (Context.Parameters.ContainsKey("username"))
            {
                serviceProcessInstaller1.Username = Context.Parameters["username"];
            }
            if (Context.Parameters.ContainsKey("password"))
            {
                serviceProcessInstaller1.Password = Context.Parameters["password"];
            }
        }
Posted: Jul 18 2009, 06:47 PM by pmiller | with no comments
Filed under:
What Lies Beneath (your csproj file & TFS builds)

We’ve recently setup an automated build to our source tree for a project I’m working on.  The transition took some time as the process of creating an automated build revealed various inadequacies in our project and solution structure. But after a bit of effort, it was all working and we moved on.

Or so we thought; we started getting errors about not being able to find a type when running certain services locally. We checked the type names and even used the assembly binding log viewer fuslogvw to find where the class loader was looking for the types.

It was looking in $ProjectRoot\bin. And our project settings window showed as a build output location of \bin as well. However, when we looked on disk, the DLLs were getting output to bin\debug. Why was VS ignoring our settings?

As usual in the land of programming, this was not magical behavior, just behavior we didn’t understand. We dug into the csproj file and discovered that we had been hasty in adding some custom build directives to our csproj files for the automated build.

The offending lines, which we had copied and pasted in all of our csproj files were:

<OutputPath Condition=" '$(TeamBuildOutDir)'=='' ">bin\Debug\</OutputPath>

<OutputPath Condition=" '$(TeamBuildOutDir)'!='' ">$(TeamBuildOutDir)assembly</OutputPath>

I underlined the problematic value for your convenience. I was annoyed to have to spend any time solving this issue, so hopefully this cautionary tale will save you some time in the future.

Posted: Jul 11 2009, 02:05 PM by pmiller | with 1 comment(s)
Filed under: