Powered by discountASP.NET
referal ID: sdtref
Why recommend discountASP.NET?

Archives
Steve Trefethen Steve's RSS Feed Subscribe or via email
What's this?
Contact me Send mail to the author(s)
About Me
View my LinkedIn profile

Add to Google
Subscribe with Bloglines
MCP Microsoft Certified Professional

Falafel Software
Online or OnSite TestComplete Training
Blogroll
Recent Comments
My Online Tools
Stats
Total Posts: 472
This Year: 77
This Month: 2
This Week: 1
Comments: 1660
Tags
Disclaimer
The posts on this weblog are provided �AS IS� with no warranties, and confer no rights. The opinions expressed herein are my own personal opinions and do not represent my employer�s view in any way.
My most popular blog posts (Q1 2008)
# Friday, June 13, 2008

Continuous Integration means developers doing command line builds

Posted @ 9:07AM

Categories: Continuous Integration | Tips

Tags:  | 

Building from the command line != building from the IDE

I just spent the last several hours fixing a large number of assembly references in a big C# solution. That means lots of errors like the following that leave you grep’ing your way through .sln/.csproj files to figure out what’s gone wrong:

Solution file warning MSB4051: Project {A9E00AF7-A1E0-4E65-90EF-CB66E8580
9AE} is referencing a project with GUID {958E0376-0272-4149-A1CF-E03521D12A72}, but a project with this GUID was not found in the .SLN file.

And builds stats that look like this:

CruiseControl.NET build statistics

Not good. It doesn’t help when VS.NET itself can cause these problems yet continue on blissfully. What I ran into today was a different reason for the same error message, this time around it wasn’t a missing "EndProject". The issue was that the second GUI was referring to a project that not only wasn’t in the .sln file but no longer existed. Regardless, the outcome was the same, building from VS.NET worked but executing MSBuild from the command line failed. WHY?! IMO, VS.NET should have failed just like MSBuild because it’s not doing you any favors when it doesn’t fail consistently along with MSBuild rather that’s just masking a problem. In addition, the error could be improved to show better information about the GUIDs that do reference a project rather than the GUID itself. I couldn’t agree more with Jeff Atwood’s post title The F5 Key Is Not a Bulid Process where he says:

If your "build process" is the F5 key, you have a problem. ...

Get your build process out of the IDE and into a build script. That’s the first step on the road to build enlightenment.

Amen.

Communicating Continuous Integration Requirements

If you’re chasing the holy grail of CI and working to get automated builds, test execution, static code analysis, build stats etc. make sure the developers working on the project understand the build requirements of your CI setup. In this case, building the main project is about as straight forward as it gets:

%SystemRoot%\Microsoft.NET\Framework\v3.5\msbuild /t:clean mysolution.sln
%SystemRoot%\Microsoft.NET\Framework\v3.5\msbuild /t:build mysolution.sln

Like I said, simple. Now, on the build server itself things aren’t quite this simple but for the bulk of the build if you get by this step you’ve gone a long way as it builds 22 C# projects. The first line above, that cleans the build is crucial. If you skip it and build from the command line after building in VS.NET you’ll continue masking the problem. A successful clean process is just as important as a successful build process. On this particular project there was a major project renaming that introduced a slew of errors but the VS.NET IDE sat idly by and in fact allowed this problem to occur. Eventually a checkin that trigger this set of failures occurred all the while "it builds fine on my machine".

The second problem was solution file name changes, copy/renames to be more specific, which further broke the CI build. The third problem was that the old .sln and csproj files were not deleted from the repository which led to even more confusion but I digress, that’s life in the real world, we fix it and move on.

# Tuesday, March 04, 2008

CCNET based EDI Invoicing Project Goes into Production

Posted @ 12:50AM

Categories: Continuous Integration | Development | Open Source

Tags:  |  | 

Over the past six months I’ve been working on a system to automate invoicing using EDI. The system is built on CruiseControl.NET and a collection of custom written source control providers and CCNET tasks. Back in December, I blogged about this project which integrates with a custom ERP system built from scratch by Falafel that processes half a billion dollars worth of transactions a year so it’s nice to finally see my small part come online.

There are several Open Source projects used in the implementation including:

Here’s a diagram of the system with CCNET at the heart:

EDI Invoicing using CruiseControl.NET

One of the many benefits of using CruiseControl.NET is the visibility the system can provide to the people in Accounts Receivable. Not only can they see what’s going on via the CCNET web dashboard but they can even install CCTray and monitor from their desktop as invoices are processed throughout the day. On the web dashboard clicking through to a specific "build" log for a given EDI 810 "build" provides invoice details including invoice number, customer name, date and dollar amount.

EDI CCNET Web Dashboard
As you can see I still need to customize the dashboard to fit into the web-based ERP system’s look and feel.

CCNET’s plug-in architecture made it perfect for this situation as it has numerous features that can be leveraged to create this, perhaps unusual, usage of a Continuous Integration server. I suppose CI could just as easily stand for Continuous Invoicing. :-)

EDI 850 Up Next

My next task will be to re-work on an existing, read old, EDI 850 Purchase Order set of processes and integrate them into CCNET. Anyway, this has been a fun project to work on and I’ve learned a lot along the way and I’d like to give a tip 'O the hat to John Waters, Falafel’s CTO, for all his help. If you’re looking for an ERP guru, I think you’d be hard pressed to top John plus he’s got a great sense of humor. One afternoon while driving back through the farm land of Watsonville from our client’s site, one of the largest organic food companies in the world, he quipped:

Here we go driving through our business objects...

Something only another geek could really appreciate!

Lastly, I have to admit after 15 years of doing product development feels very satisfying to design and implement a system that solves a real world business problem. There’s just no substitute for hands on experience!

Related posts:

[UPDATED: March 4, 2008] Forgot SubSonic which I use for data mapping/xml creation!
# Wednesday, February 06, 2008

An FTP Source Control Provider for CruiseControl.NET

Posted @ 12:20AM

Categories: Continuous Integration | Open Source

Tags:  | 

I've been working on a custom CCNET implementation to handle EDI invoicing which is nearly complete and one of the things I needed was FTP download support. In the project I'm working there is a utility that makes use of edtFTPnet, from Enterprise Distributed Technologies, an Open Source C# FTP library licensed under LGPL. It seems to work reasonably well though the authors are admittedly "love the 'internal'" keyword thus making extensibility difficult, if not impossible, short of forking the codebase. Anyway, I've been using the library and it seems to work just fine. Btw, I've submitted my suggested changes so hopefully they'll recognize the benefits and incorporate them.

Writing Plugins for CruiseControl.NET

Anyway, what I needed as a mechanism that would poll an FTP server looking for new files to be downloaded which sounded like a reasonable thing for CruiseControl.NET. Of course, once files are downloaded additional processing is done so it fits with CI quite nicely. Fortunately, CruiseControl.NET has a nice plugin architecture that makes this sort of thing easy.

Creating a Plugin Assembly

The first step is to create a new Class Library following the naming convention ccnet.*.plugin.dll and add the following assembly references:

  • edtFtpnet
  • Thoughtworks.CruiseControl.Core.dll
  • NetReflector.dll

And add the following to your using statements:

  • System.Collections
  • EnterpriseDT.Net.Ftp
  • Exortech.NetReflector
  • ThoughtWorks.CruiseControl.Core
  • ThoughtWorks.CruiseControl.Core.Util

Your assembly should appear as follows (click the image for a larger version):

CCNET plugin assembly

Adding ISourceControl

The next step is to add the ISourceControl interface and provide the necessary attribute which will declare the name used to identify your Source Control Provider.

[ReflectorType("ftpscc")]
public class FTP: ISourceControl

The ReflectorType attribute comes from the NetReflector assembly and is located in the Exortech.NetReflector namespace. The value provided to the attribute is the name which will be used in ccnet.config as follows:

<sourcecontrol name="ftpscc"></sourcecontrol>

Adding FTP Specific Properties

Next, add properties for the FTP connection and once again we'll use attributes from Exortech.NetReflector making it easy to declare settings from ccnet.config:

[ReflectorProperty("deleteRemoteFiles", Required = false)]
public bool DeleteRemoteFiles = true;

[ReflectorProperty(
"fileMask")]
public string FileMask;

[ReflectorProperty(
"localPath")]
public string LocalPath;

[ReflectorProperty(
"password")]
public string Password;

[ReflectorProperty(
"remoteHost")]
public string RemoteHost;

[ReflectorProperty(
"timeOut", Required = false)]
public int TimeOut = 60;

[ReflectorProperty(
"userName")]
public string UserName;

[ReflectorProperty(
"remoteDir", Required = false)]
public string RemoteDir = "/";

These properties indicate settings for the FTP connection as well as the location where the files should be placed after being downloaded. Additionally, there is a flag which indicates if the downloaded file should be deleted from the server.

Again, simply adding the ReflectorProperty attribute will cause these members to be populated with the data from your <sourcecontrol name="ftpscc"> tag in ccnet.config.

Implementing ISourceControl

Next, implement the ISourceControl interface on your class. Of course, the easiest way (in VS.NET) to stub out the necessary methods is via the context menu with the caret sitting on ISourceControl:

VS.NET Implement Interface

This will give you five new methods though I'll focus on the following two since that's all you really need to implement here:

public Modification[] GetModifications(IIntegrationResult from, IIntegrationResult to)

public void GetSource(IIntegrationResult result)

In GetModifications we need to return an array which contains information about the files to be downloaded and in GetSource actually fetch the files. One caveat here is we use DateTime.Now as the modified time so that CCNET will consider the file as a "new" change. So, GetModifications will look like this:

1 public Modification[] GetModifications(IIntegrationResult from, IIntegrationResult to)
2 {
3 ArrayList mods = new ArrayList();
4 Connect();
5 try 6 {
7 Log.Debug("Fetching list of files that match: " + FileMask);
8 FTPFile[] mModifiedFiles = ftp.DirDetails(FileMask);
9 Modification mod;
10 int i = 0;
11 foreach (FTPFile f in mModifiedFiles)
12 {
13 mod = new Modification();
14 mod.Type = "Added";
15 mod.FileName = f.Name;
16 mod.ChangeNumber = i;
17 mod.ModifiedTime = DateTime.Today;
18 mods.Add(mod);
19 i++;
20 }
21 return (Modification[])mods.ToArray(typeof(Modification));
22 }
23 finally 24 {
25 // Close the connection if there are no files to process 26 if (ftp.IsConnected && mods.Count == 0)
27 ftp.Quit();
28 }
29 }
30

GetSource handles downloading files from the server and then deleting them:

1 public void GetSource(IIntegrationResult result)
2 {
3 try 4 {
5 foreach (Modification m in result.Modifications)
6 {
7 Log.Debug("Fetching file: " + m.FileName);
8 ftp.Get(LocalPath, m.FileName);
9 if (DeleteRemoteFiles)
10 {
11 Log.Debug("Deleting remote file: " + m.FileName);
12 ftp.Delete(m.FileName);
13 }
14 result.AddTaskResult(string.Format(
15 "<fileDownloaded>{0}</fileDownloaded>\n", m.FileName));
16 }
17 }
18 finally 19 {
20 ftp.Quit();
21 }
22 }

Connecting to the FTP Server

The call to Connect in GetModifications above looks like this:

1 public void Connect()
2 {
3 Log.Debug(String.Format("Connecting to remotehost: {0}...", RemoteHost));
4 ftp.Connect();
5 Log.Debug(String.Format("Connected, logging in with user: {0}", UserName));
6 ftp.Login(UserName, Password);
7 Log.Debug(String.Format("Switching to remote directory: {0}", RemoteDir));
8 ftp.ChDir(RemoteDir);
9 }

The only thing I haven't really covered here is the construction of the FTPClient member and the assignment of the its RemoteHost and Timeout properties which I think is pretty straightforward.

Conclusion

Writing plugins, whether for Source Control Providers or tasks (ITask), the process is pretty straightforward though there is little documentation on the subject. Be prepared to spend some time studying the other ISourceControl and ITask implementations included with CCNET and quite a bit of time debugging CCNET itself to understand how it works. One issue I've run into and that I've blogged about before which is when exceptions occur from within a Source Control Provider. In the current released version of CCNET (1.3) exceptions are not handled by the publishers for the project therefore you'll never get notified about connection problems etc. unless you use the log4net settings I mentioned.

The above implementation is lacking in a number of areas, like error checking, and is a very simplified example of code I'm using in production. I've also tweaked CCNET itself so that any exceptions raised within my FTP provider are handled by the build publisher so that I can get email when things fail. In fact, I've been working to get these, or similar, changes incorporated into CCNET.

[UPDATE: Feb 6, 2008] Fix image links
# Monday, January 28, 2008

Using the Delphi command line compiler on a Continuous Integration server

Posted @ 11:25AM

Categories: Continuous Integration | howto

Tags:  | 

Recently, I got an email asking how to deploy the Delphi command line compiler for use on a continuous integration (CI) server. Since I’m no longer working at CodeGear nor coding in Delphi I forwarded the message to a few guys at CodeGear mentioning that it would be a good blog post but alas, I never got a response. Geeze, I didn’t think it had been that long guys! :-) Anyway...

I figured the question was worth a post if for no other reason than to start a conversation so other Delphi developers could contribute with their experience. Before we begin be sure to read your Delphi software license as I seem to recall there being some sort of issue related to the license regarding compiler deployment.

Getting the command line compiler to a build machine should be pretty straight forward though I’ve never had a need to do it myself since I was always working with the entire source tree including the compiler.

Basically, what’s needed is the Delphi compiler and the DCU/DCP’s from the products "lib" directory which means.

  • DCC32.EXE
  • The lib directory from your Delphi IDE source path

Aside from that here are some other suggestions for a build machine:

  • Using command line options (or a dcc32.cfg file) always specify an output path for the compiler, never place project related output in the same directory as DCU/DCP’s that are shipped with Delphi
  • Make sure you do a "clean" before each new build meaning run a process that deletes all of the compiler produced output from the previous build and verify that it actually works!

Be sure to read Language Reference Guide as I recall working with Nathan Tawill years ago on it and it was very good.

Lastly, another option would be to simply install a copy of the product on the build machine. Oh yeah, be sure to ask CodeGear to create an install that will take care of all this for you.

See also:
Other continuous integration posts
Video: Setting up a continuous integration environment

Ok, that’s my quick and dirty write up, your turn, what’s missing?
# Wednesday, January 16, 2008

CruiseControl.NET features beyond v1.3

Posted @ 8:03AM

Categories: Continuous Integration | Open Source

Tags:  | 

The last official release of CruiseControl.NET was v1.3 from June 2007 but that doesn’t mean there hasn’t been ongoing work. I’ve been updating my CCNET sources recently and looking at some of the new features which include:
  • devenv task updated to VS.NET 2008
  • ExternalSourceControl source control provider
    <sourcecontrol type="external"> <executable>(path to command-line application)</executable> <args>(command-line parameters for application</args> <autoGetSource>(true/false like other sourcecontrol providers)</autoGetSource> <labelOnSuccess>(true/false like other sourcecontrol providers)</labelOnSuccess> <environment> <var>name=value</var> (environment variables to pass to the provider) <var>name=value</var> </environment> </sourcecontrol>
  • FilteredSourceControl block for specific comments / labels
  • Expand notification of modifying users to support more than one status (e.g., "Failed" + "Fixed" so you get both the bad news and the good news).
  • PlasticSCM source control support
  • Improved standard statistics report page
  • RSS feed for build status
  • RegEx support in the email publisher to allow for utilizing source control usernames as email addresses among other things
  • Lots of bug fixes in CCNET as well as CCTray
These are just some of the items I thought worth noting while perusing the SVN change log. There are lots of other changes, fixes etc. If you’re looking for CCNET improvements you might want to try and grab the source. I found building the tree to be quick and painless.