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"];
}
}