|
LATEST POSTS
Tuesday, April 07, 2009
I’m going to go out on a limb and posit that a few of you have attempted, and possibly succeeded, at integrating JsUnit into an automated build process. Assuming that is true, you people are off my Christmas card list for not blogging about it in such a way that it is discoverable by Google. (And that includes you, Miller). The sole exception to this is honorary hillbilly Toby Henderson who provided enough insight in the comments to Jeremy Miller’s post mentioned so disparagingly above. From that, I was able to come up with the following NAnt task (and Jayzus only knows how this is going to look in an RSS reader): <target name="jsunittests"> <property name="jsunit.port" value="9123" /> <property name="jsunit.authority" value="localhost" /> <property name="jsnuit.vdir" value="AdventuresInPlaid" /> <property name="jsunit.acceptor.partialurl" value="${jsunit.authority}:${jsunit.port}/jsunit/acceptor" /> <property name="jsunit.url" value=""http://${jsunit.authority}/${jsunit.vdir}jsunit/testrunner.html?testPage=${jsunit.authority}/${jsunit.vdir}JsUnitTests.aspx&autoRun=true&submitresults=${jsunit.acceptor.partialurl}"" /> <property name="jsunit.browserFileNames" value=""${programfiles.dir}\internet explorer\iexplore.exe,${programfiles.dir}\Mozilla Firefox\firefox.exe"" /> <property name="jsunit.log" value="logs" /> <property name="jsunit.resourceBase" value="" /> <property name="libs" value="" /> <foreach item="File" property="filename"> <in> <items> <include name="app\BookedIn.Web\JsUnit\java\lib\*.jar" /> </items> </in> <do> <property name="libs" value="${libs}.\lib\${path::get-file-name(filename)};" /> </do> </foreach> <property name="libs" value="${string::substring(libs, 0, string::get-length(libs) - 1)}" /> <exec program="java" workingdir="app\BookedIn.web\jsunit\java" commandline="-Dport=${jsunit.port} -DbrowserFileNames=${jsunit.browserFileNames} -Durl=${jsunit.url} -DlogsDirectory=${jsunit.log} -classpath ".\bin\jsunit.jar;.\config\;${libs}" junit.textui.TestRunner net.jsunit.StandaloneTest" /> </target> Assuming that’s copy-and-pastable into your own script, I’ll move on. The properties almost certainly should be moved outside of this target, of course. I’ve included them all here for convenience. Some of them are machine specific so putting them at the top means you can override them with a local properties file. The <foreach> loop in the middle was borne out of frustration. It will look through all the .jar files in the JsUnit lib folder and create a semi-colon-delimited list of them. This is then passed into the classpath for the <exec> task which launches the whole thing. My guess is that Toby used an older version of JsUnit because it appears there are a lot more libraries involved now. After about the third or fourth time running the task with a manually-entered list of libraries and having a ClassDefNotFound error come up, I sought out some help to just dump them all in with the <foreach>. Word of caution: the config folder included in the classpath contains xwork.xml. The part that caused us the greatest trouble was a message “Error loading configuration file xwork.xml”. This was because we were including the path to the xwork.xml file explicitly, rather than the path to just the config folder. It was the Ant file for JsUnit itself that eventually led to our salvation. So between this and the recent announcement that my wife is pregnant, all is well in Hillbilly House. Kyle the Expectant
Monday, February 16, 2009
Side note: I really miss my “, or How to…” blog post titles. The title should have narrowed down the focus for you already. I’ve been bear wrestling with some OSS projects on Team City for a few weeks now. It seems NAnt has some issues with a brand new Windows Server 2008 (64-bit) with .NET 3.5 SP1 (and possibly the .NET SDK). Specifically, it doesn’t recognize that .NET 3.5 is installed and will try to use .NET 2.0 by default. I say “seems” because this is all working theory. For almost every project I looked at that used NAnt, I had to configure it to use –t:net-3.5 in the command line parameters to force it to use .NET 3.5. And even then, I still ran into a pretty nasty issue with one of the projects. The error was something along the lines of “Microsoft .NET 3.5 is not installed, or not correctly configured.” After several installs/re-installs of various frameworks and SDKs, I finally broke out the NAnt source code and traced it to the installRoot and sdkInstallRoot properties from NAnt.exe.config. I’ll cut this off by saying a link is worth a thousand words. The two aforementioned properties pointed to the HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.0a but the key in the server was HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.1. As suggested in the article, I faked it out by manually adding the keys it was looking for with values I deemed appropriate… …and it *still* didn’t work. “Lord Tunderin’ Jayzus,” says I and because I was sufficiently peeved, I added “and Moses on a boat!” Try as I might, I could not get NAnt to read the registry key. I cussed out deities in all major religious (except Buddhism; eternal damnation I can deal with, karma kinda scares me) until I discovered a link worth TWO thousand words. Somewhere in the depths of 64-bit land, Windows is picking up requests to HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.1 and redirecting them to HKLM\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v6.1. So I duplicated the appropriate key once again and I’m shiny and happy and able to sleep once more. Note that this is expected to be fixed in some upcoming release of NAnt (and very well may be addressed already). But each of the projects on the server uses NAnt in a different way, some including it in their code repository itself, and I am not in the position to upgrade NAnt in any of them. Special shut-out to Tim Barcz for the link that, while worth only about 850 words tops, got me on the right track nonetheless. Kyle the Karmic
Monday, March 03, 2008
In my last post, I ran on about automated releases. So much so that adding automated deployments at the end seemed like cruel punishment. So instead, I decided to make it a separate post. Still a cruel punishment, but now it is unusually so. To recap, our automated release target executes on every check-in and creates a subfolder under the releases folder with the version number of the application. Then a releaseable "package" is placed in it. In this case, it's a web application that is to be deployed. The target you are about to see follows from this train of thought. And apologies if the formatting looks off. I've yet to figure out how to embed XML into a post in LiveWriter or Community Server's editor so that it uses the right HTML in the browser and the average RSS reader. <target name="deploy" depends="version">
<deliisdir vdirname="${deploy.vdir}" failonerror="false" />
<delete dir="${deploy.dir}" failonerror="false" />
<mkdir dir="${deploy.dir}" />
<copy todir="${deploy.dir}" >
<fileset basedir="${latest.release.dir}">
<include name="**\*.*" />
</fileset>
</copy>
<property name="database.name" value="${database.name.test}" />
<call target="drop-database" />
<call target="create-database" />
<mkiisdir dirpath="${deploy.dir}" vdirname="${deploy.vdir}" defaultdoc="Default.aspx" />
<loadtasks assembly="${tools.dir}\nant\Vitreo.Nant.dll" />
<iisappmap vdirname="${deploy.vdir}" extension=".mvc"
executable="C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll"
verbs="GET,POST"
checkfileexists="false" />
</target>
As before, a list of variables:
| deploy.vdir |
The name of the IIS virtual directory we are deploying to |
| deploy.dir |
The folder that the virtual directory will point to |
| latest.release.dir |
Path to the folder containing the latest release. e.g. C:\projects\LineageOrganizer\releases\Latest |
| database.name.test |
The name of the database we are pointing to in the deployed environment (i.e. the Test environment) |
| tools.dir |
Path to tools used during the build process. e.g. C:\projects\LineageOrganizer\tools |
And again, a brief synopsis of what the target does:
- Delete the existing IIS virtual directory if it exists. (The <deliisdir> and <mkiisdir> tasks are included in NAntContrib)
- Delete the deploy folder if it exists and re-create it.
- Copy the latest release to the deploy folder.
- Drop and re-create the test database (the drop-database and create-database targets use the <updateSqlDatabase> task from vincent-vega, which appears to be off-limits today but you can get a copy from CodeCampServer).
- Re-create the IIS virtual directory
- Create a mapping for the .mvc extension for ASP.NET MVC. (<iisappmap> task is courtesy of Brian Donahue, who I'd recommend adding to your blogroll).
The last step is necessary only if you need to create an IIS mapping, which you do when deploying an ASP.NET MVC application into an IIS 6 environment. Otherwise, you can skip this step.
In the post on releases, I snuck in a teaser on the folder where I store the latest version of the application. In that target, I created a folder with the same name as the application version and released to it. Then I copied the entire contents of this folder to another one called Latest. The reason for this will be clear shortly. First, I need to talk about when this target is executed.
The deployments don't happen at the same interval as the build. That is, the application is not deployed on every build. This would be pretty chaotic if you re-deployed the application several times a day while the testers were trying to work.
So they have to be on a different schedule or, as in my case, triggered manually. This means setting up a separate CruiseControl.NET project for them. So for our LineageOrganizer application, we would have two CC.NET projects: one for the main build and one to deploy the application. Both use the same build file and both have the same root folder. In fact, the CC.NET project for the deploy is relatively simple:
<project name="LineageOrganizer.deploy">
<webURL>http://ccnetservername/server/local/project/LineageOrganizer.deploy/ViewProjectReport.aspx</webURL>
<workingDirectory>C:\data\ccnet\Trilogy.Gunton</workingDirectory>
<triggers />
<tasks>
<nant>
<executable>c:\data\ccnet\Trilogy.Gunton\tools\nant\nant.exe</executable>
<buildFile>Trilogy.Gunton.build</buildFile>
<targetList>
<target>deploy</target>
</targetList>
</nant>
</tasks>
</project>
There's no <sourcecontrol> tag nor are there any merge files to take care of. Just execute the target and that's it.
Here's where the concept of a Latest folder comes in. The releases are created based on the application's version number. And the version number is based partially on the CruiseControl version. But now we have two different CC.NET projects, each with their own version number in CruiseControl. So the latest version for the main build, which is the one that creates the releases, is different than the one that deploys the application. I.E. If we try to get the path to the latest release folder based on the version number within the deploy target, it would create a path based on the deployment CC.NET project, not the original one.
So to get around this, after every release, we copy the release to a statically named folder called Latest. That way, both the build CC.NET project and the deploy CC.NET project know where it is.
Another way to do this would be to write the current version number to a file. This was going to be my first course of action until Donald suggested the static Latest folder which I like better although only because it feels better.
This process is still a work-in-progress for me but it's worked out reasonably well so far on my one-man team. Though I will admit that the deploy target does fail on occasion if someone is using the app when it is running. Seems to be locking files so that the deploy folder can't be deleted/re-created. When that becomes annoying enough to fix, you can be sure I'll pad my blog resume with the solution.
Kyle the Deceptively Simpleww
Monday, February 11, 2008
The Hillbilly asks: How do you compile your application in your NAnt script for releases? There are many options, you see, even though the <solution> task is no longer one of them. You could use the <msbuild> task to build the solution as it is built in VS (I think; the documentation is a little sketchy on this task if you don't know the inner workings of msbuild). Or you could use the <csc> or <vbc> tasks to compile a group of code files into an assembly (similar to a .csproj or .vbproj file). Or, for the ultimate control, you could use the <exec> task to call out to msbuild.exe, csc.exe, or vbc.exe directly though I couldn't tell you personally what that offers you that the corresponding tasks don't. Future-proofing maybe. Usually, for testing purposes at least, this entails using <csc> (or <exec> with csc.exe) to compile all the application code into a single assembly that I can run tests against. It's a practice I learned from JP Boodhoo and I've been happy with the results in the absence of anything better. But I don't care much about the unit tests. I'm more interested in your releases. Assuming you've automated them in a NAnt script, how do you build the application in preparation for them? Historically, I've usually used the <msbuild>/msbuild.exe scenario mostly because I couldn't wrap my head around writing a bunch of <csc> tasks to compile the code into the same structure I see in Visual Studio. For example, if your solution has a Domain project, a DataAccess project, and a UI project, I would need three <csc> tasks to create the individual assemblies. Or one <msbuild> task running against the solution. My reasoning was that it was the easiest way to build the application the same way I did in Visual Studio, which I assumed was how I wanted to deploy it. Then I talked to Donald Belcham. Conversations with Donald Belcham are always eye-opening (and I won't expand on that comment this time; happy belated birthday, dude). And during our chat, he admitted to using <csc> to create his releases. I expressed my surprise: "But Iggy, what if your solution has a dozen projects in it? I don't want to deal with the pain of a dozen <csc> tasks in the right order". To which he replied: "Dude, you don't need to deploy the same way you build. The Visual Studio solution is a file management mechanism, not a deployment mechanism." And my mind awakened like it did during my last visit to Brian Head. The basic idea Donald described was thus: Just because you build your application one way in Visual Studio during development, doesn't mean you need to build your releases the same way. Especially if you are using NAnt to create your releases because then you can do it however you like. And as if my mind wasn't expanded enough, he typically shoots for one assembly per physical location. Not one assembly for the domain, one for the data layer, one for the common utilities, and so on and so forth. One. Total. If I were building a WinForms application with no other physical layers, I'd have one file: MyApp.exe. If it were a WinForms app calling a web service, there would be two assemblies. One for the client and one for the web service (plus corresponding .asmx files). This is the extreme position of course and there are caveats. If the application depends on third-party libraries, naturally we won't bundle those up into the assembly. And if you're using an internal client library that is common to more than one project, you'd treat that as you would any other third-party library and keep them separate. But if your application-specific domain code and data access code sits on the same server, that all gets munged into one assembly regardless of how it is organized in the .sln file. NAnt gives us that flexibility. I'm kind of putting Donald out there for potential ridicule so I'll expose myself ever so slightly and admit that I really like this idea. Why *do* we feel we have to ship a dozen assemblies for our application? If something changes in our domain, what difference does it make if we re-deploy MyCompany.MyApp.Domain.dll or MyCompand.MyApp.exe? And in speaking with Donald, he points out that even though we have the flexibility to deploy only parts of an application, rarely do we exercise it. In a typical scenario, new versions and hot fixes are deployed as an "all or nothing" in each physical layer anyway. This matches my experience. Even if nothing changed in certain assemblies, I typically re-deploy everything during an upgrade just to be safe. Now, we still need to be practical about it. If the resulting executable is 300Mb, then ISDN line or not, you'll want to break that into something more manageable. But for the average application, one assembly should function just as easily as five, yesno? Looking forward to hearing the counter-arguments because I haven't quite thought this through too much except from a maintenance point of view. In particular, are there performance differences with loading several smaller assemblies vs. one "larger" (but still manageable) one? What about issues with unloading an assembly from an application pool in IIS? Enquiring minds want to know! Kyle the Inquisitive
Disclaimer
The opinions expressed herein are my own personal opinions and do not represent
my employer's view in any way.
Copyright © 2010 Kyle Baley. All rights reserved.
|
|
|
CATEGORIES
|
|
LATEST POSTS
POPULAR POSTS
|
|
ARCHIVE
| March, 2010 (1) |
| February, 2010 (8) |
| January, 2010 (2) |
| December, 2009 (3) |
| November, 2009 (2) |
| September, 2009 (5) |
| August, 2009 (4) |
| July, 2009 (2) |
| June, 2009 (5) |
| May, 2009 (6) |
| April, 2009 (5) |
| March, 2009 (6) |
| February, 2009 (2) |
| January, 2009 (6) |
| December, 2008 (5) |
| November, 2008 (2) |
| October, 2008 (5) |
| September, 2008 (9) |
| August, 2008 (5) |
| July, 2008 (7) |
| June, 2008 (6) |
| May, 2008 (11) |
| April, 2008 (13) |
| March, 2008 (13) |
| February, 2008 (12) |
| January, 2008 (19) |
| December, 2007 (16) |
| November, 2007 (8) |
| October, 2007 (23) |
| September, 2007 (15) |
| August, 2007 (8) |
| July, 2007 (6) |
| June, 2007 (11) |
| May, 2007 (19) |
| April, 2007 (14) |
| March, 2007 (3) |
| February, 2007 (4) |
| January, 2007 (7) |
| December, 2006 (5) |
| November, 2006 (9) |
| October, 2006 (11) |
| September, 2006 (14) |
| August, 2006 (11) |
| July, 2006 (15) |
| June, 2006 (8) |
| May, 2006 (10) |
| April, 2006 (12) |
| March, 2006 (3) |
| February, 2006 (7) |
|
|
|