|Expert .NET Delivery Using NAnt and CruseControl.NET|
|summary||Automate your .NET software build and delivery process|
Disclaimer: I got this book free as a giveaway for our .NET Developers Group. Some folks might think this could influence my opinion, but they'd be wrong. Also, Marc Holmes is in no way related to me. (Well, we're both humans inhabiting the Earth, descended from Adam and Eve or the same biological soup depending on your beliefs, so I suppose that's not completely true.)
Note: To avoid possible confusion due to our same last names, through the review I'm going to refer to the author as "Marc ," not "Holmes."
I got this book because I'm an independent software geek who loves having automated, repeatable processes. I don't have a QA department to double-check every drop I send to customers, nor do I have a separate process department to run checklists every time I need to gather up some statistics on testing, code complexity, etc. I need all these sorts of tasks wrapped up into stable, repeatable, automated processes so that I don't have to constantly worry whether I've forgotten something. (Worse yet, I work from home while taking care of two young children. Automated processes let me focus my meager remaining concentration on software construction while having stuffed animals tossed on my keyboard and drums bashed behind my work chair.) I've had a solid background in Ant and NAnt, but wanted real-world detail on how to generate an end-to-end process for continuous integration and delivery.
This book is part of Apress' "Expert" series which includes books on Oracle, .NET development, web services, and Network Time Protocol. (And who among us hasn't frequently needed an expert book on NTP when we're trying to remember some details of symmetric passive mode?)
Two mantras run centrally through this book: Marc's quip "Design for Delivery," and the importance of standards-based processes. It's not enough to cobble together an automated system which may get one from end-to-end. A team needs a system which gets software built, tested, reported, and packaged in a form read to drop into a customer's environment. Having the mindset of "Design for Delivery" helps focus the team on meeting the goal of getting their software out the door in best fashion. Approaching an automated build system with a thought to standardization means that the team shoots for systems applicable to many projects. Each new project's solution shouldn't require a large amount of rework to get the build/deploy system up and running. Marc is emphatic about this throughout the book, constantly refactoring build and deploy scripts to keep them as abstract as possible.
Marc approaches the task of creating an automated delivery process with the same mindset of designing software: a few use cases with expected outcomes which are used as guidelines for building up the various scripts needed to get delivery tasks done, standards for wider implementations are considered, then the scripts themselves are built in an iterative fashion.
This book isn't a fluff-filled overview of tools. Marc dives down deep into the guts of several very useful tools, showing readers how to solve tough, real-world problems. Simplistic build and deployment systems which do nothing more than run a compiler and zip the resulting output are, well, simple to build.
Theory and details of how a tool (or tools) might be used are fine, but then reality in implementation hits. Marc matter-of-factly states that his approach to a delivery system may not work for everyone. Many of the tools he demonstrates offer multiple approaches to solving problems, such as NAnt's ability to use different build files via inclusion or as separate targets. Marc almost always discusses these options and discusses the rationale for his selection.
Marc also includes some great "Further Reading" sections at the end of most chapters. He points readers to some terrific additional reading such as McConnell's Code Complete, 2nd ed., Ambler's Agile Database Techniques: Effective Strategies for the Agile Software Developer, and Newkirk's Test Driven Development in Microsoft .NET.
The book's flow is very sensible and straightforward. Marc opens with a good discussion on creating a delivery system, then moves on in chapter two to a example of one company's project which he carries through the entire book. The example project is expanded during subsequent chapters as Marc builds up the build and deployment scripts as he covers that chapter's topic. I found this particularly useful since it's a great guide to building one's own automated system. It's easier to follow one example through an evolutionary process rather than having different examples thrown out piecemeal.
The book's laid out in ten chapters plus two appendices. After the first chapter's introduction, each chapter covers one key concept in the build system. Chapter ten closes out the book with "Closing Thoughts."
Chapter 1, "A Context for Delivery," lays out Marc's ideas on why automated, standardized build systems are so critical. Marc doesn't waste time detailing examples of train wrecks due to bad delivery processes. He has a short blurb on the business benefits of automation, then gives an overview of his example company, Etomic, and its products.
Marc continues by discussing potential processes for delivery, covering potential problem issues with each option, laying out his case for standardization and automation in the delivery cycle. Readers who are looking for rescue from a chaotic build and delivery process should hopefully have an epiphany moment or two in this section. So may readers who already have some process in place.
Chapter 2, "Dissecting NAnt," gives an introduction to NAnt and discusses its basic features. The ubiquitous "Hello, World" example is used, then the chapter moves on to discuss the details of creating build files, and variations for invoking NAnt from the command line. There's some good detail on using loggers to generate and merge output from NAnt into an XML log file, important for tracking exact execution details. NAnt's all-important properties, configuration file options for controlling NAnt's execution, are also covered in good detail.
Marc finishes the chapter by creating a skeleton build file for the fictitious Etomic corporation. This skeleton is expanded upon in following chapters as Marc discusses other tools and processes.
Chapter 3, "Important NAnt Tasks," is where Marc gets into the weeds of NAnt's execution. NAnt tasks are chunks of functionality contained in NAnt's libraries. Tasks give NAnt users support for things like interfacing to CVS for source code control, calling NDoc to create documentation from XML comment files, and reading values from the Windows registry. Marc selects several groups of tasks to cover, including conditional tasks for controlling build flow (if, ifnot, fail, e.g.), file management (attrib, copy, mkdir, get), and the fundamental build tasks (asminfo, exec, mkiisdir, solution, csc).
Marc also introduces NAnt-contrib, a second library of tasks written by other NAnt community developers. These tasks, which haven't yet made it into NAnt's framework, provide critical, additional functionality such as interfacing to Visual Source Safe for configuration management.
While he details important tasks in clear fashion, Marc makes it clear that his book is not a reference for the tools he covers. He emphatically points users to the tools' sites for more current and detailed information.
Marc carries this theme throughout the book: he focuses on what's necessary to get the job done, briefly describes potential enhancements or other possibilities, then points the reader to sources for more information.
Chapter 4, "A Simple Case Study," finally gets to the "real world" implementation. Marc begins to fill in the skeleton developed in Chapter 3 with tasks for testing via NUnit, documentation via NDoc, and error handling via NAnt's 'nant.onfailure' property which points the build process to an error-handling task.
Versioning software during a build can be difficult, but Marc has a section in this chapter devoted to handling versioning. He also shows opportunities for refactoring the build file from its klunky initial form to something less brittle and more easily extended. He also leaves the build file behind to begin development of a separate deployment script. He admits his initial deployment script is overly simple and suitable only for basic Windows applications; however, he continues to enhance and expand the deployment script as the book progresses.
Chapter 5, "Process Standards ," seems misnamed to me. Marc does spend some time discussing naming conventions and source control organization at the start of the chapter; however, most of this short chapter centers on refactoring the single build file into separate chunks.
Additionally, he covers a more complex build and deployment example with a custom-written Visual Source Safe Manager component which utilizes COM interoperability and is installed as a Windows service. Both these features are some of the more complex issues one might tackle in real-world deployments, so Marc's text here is very useful.
A semi-hidden gem in this chapter is Marc's discussion of a tip for getting around the less-than-helpful structure of a Web application in Visual Studio .NET 2003. Microsoft forces developers to hack up virtual links to an IIS server's web publishing folder, then scatters Web Application files between the .NET solution's directory and the web folder. This causes great difficulty when trying to use NAnt to build and deploy web apps. Marc points readers to Fritz Onion's wiki site where a clearly explained procedure for changing VS.NET's web application behavior awaits.
Chapter 6, "Continuous Integration ," pulls CruiseControl.NET (CC.NET) into the picture. Marc starts the chapter with great discussion on the benefits of Continuous Integration (CI), then begins detailing the tools. Marc chooses CC.NET, but he also gives a short bit of coverage to two options, Draco.NET and Hippo.NET. His "Further Reading" section for this chapter also points out other options.
Marc's coverage of CC.NET is much as his coverage of NAnt: targeted, detailed discussion of the features needed only to implement his build and deployment system. He writes about the Web Dashboard and the useful cctray applications, then moves to basic configuration and setting up the server. There's brief but adequate coverage given to configuring triggers, source control integration, and publishers, plus Marc points out what changes are needed for the NAnt build scripts. Marc closes the chapter with summary screens and output from CC.NET runs, then mentions how CC.NET is easily extendable if one needs additional functionality - providing a good transition to the next chapter.
Chapter 7, "Extending NAnt" is a great tutorial on how to write your own tasks to cover jobs not in NAnt or NAnt-contrib's libraries. Marc uses 'mkdir', 'copy', 'version', and 'exec' to help readers learn the basics of writing their own tasks in NAnt. He discusses NAnt attributes, crucial to NAnt's framework, then moves to how individual tasks interact with the master build file for capturing events, reading properties, etc.
Marc's first example of extending NAnt to incorporate FxCop is educational, but has been overcome by events: FxCop support is included in NAnt-contrib version 0.85rc3. Regardless, it's very useful to see how simple it is to write one's own task in NAnt.
Chapter 8, "Database Integration" is by far the longest and intricate chapter. Right off the bat Marc lists some of the hardest problems to solve when integrating databases into a build system: lack of source control, the amount of detail one must pay attention to, and how to deal with data in the database itself.
Marc puts forth examples of shared and local database development, then moves on to build/deploy tasks involved in integrating databases. He also covers how these tasks thread into the continuous integration process. Next he covers modifying the build and deploy scripts to integrate the database tasks.
The brunt of chapter 8 revolves around wrapping Red Gate's SQL Bundle, a commercial tool package, into the processes. Marc shows how Red Gate's tools enable the process to automatically detect database schema changes, create merge or update files, and compare database instances. This chapter alone might make the book worth its price if readers are involved with any projects needing substantial database development.
Chapter 9, "Code Generation," makes use of CodeSmith, a code generation package in both freeware and commercial formats. Marc espouses code generation as a way to alleviate problems in complex build environments.
Specifically, Marc identifies three troublesome topics: separation of concern (intertwining of process and configuration information), specific system steps (unavoidable hard-wiring in of file names for NDoc's documentation, for example), and administration overhead (the work required to keep the build/deploy systems operational or portable to new projects).
Marc's use of CodeSmith demonstrates how code generation can solve these problems and wrap directly into the build process. He uses short, clear examples on how to tie CodeSmith into both NAnt and CruiseControl.NET.
Chapter 10, "Closing Thoughts," is Marc's summary. It's a walkthrough of the territory the book just covered, laying out the material in brief form. Context, motivation, mechanics, consequences (pros/cons), and results all get concise summaries. The author also lays out a very useful Best Practices list for processes, standards, NAnt, CruiseControl.NET, and other factors. He also spends a very few paragraphs on the direction Microsoft is taking with their upcoming Build Server and its potential impact on the NAnt community.
The mechanics and format of this book are terrific for the most part. The author's writing style is clear, easy-going, and humorous without being campy. The index appears to be very complete and covered the few things I needed to reference.
On the negative side, a few of the screenshots weren't framed or cropped very well, leaving me looking for a magnifying glass to try to figure out bits the author referenced in his text. However, this was the case with only a few graphics. Most were very clear and viewable.
Additionally, the versions of tools covered in this book are somewhat outdated, but the author makes that very clear in several places through the book. All his examples work for the versions referenced in the book. Additionally, a complete download of all the various build files, tools, and source code examples is available from Apress's website.
This is a terrific book for folks in the .NET arena looking to establish an automated build and delivery system. It's a very good book for folks looking to enhance an existing automated system, particularly if you're looking to solve the really difficult problem of wrapping database integration into your system.
Lastly, I'd say it's a very good book for anyone interested in automated build and delivery processes, regardless of the environment you're working in. Java developers can get great material from this book, just as I got great ideas from Loughran's Java Development with Ant.
You can purchase Expert .NET Delivery Using NAnt and CruseControl.NET from bn.com. Slashdot welcomes readers' book reviews -- to see your own review here, read the book review guidelines, then visit the submission page.