Automated Coder

Exploring the Code of CruiseControl.Net

Sorting the Grid with “Locked” Rows

Posted by Craig Sutherland on 27 January, 2010

A Simple Goal

In the dashboard there are two types of rows: the project details and the build status. For 1.5 we had removed the build status row and instead attached it as a tool-tip. However, there were a number of complaints about this – people didn’t like the way we (read I) implemented the tool tips.

So, after a quick trip back to the design board, I decided to put the build status back in as a static row. However this is where the problem starts – the project grid in 1.5 was changed to use client-side sorting (save on the round-trips to the server.) Just adding the build status gives some weird results:

image

Notice that the build status says “Setup for documentation generation” and the project that is actually building is the CCNet-Doc project. The build status line should be associated with the CCNet-Doc project, not the CCNet project! If multiple projects are building, it will put all the build statuses at the bottom of the grid (or the top if the grid is sorted in reverse order!)

So, how can this be fixed?

Some Background

Before I move onto how I resolved the problem, first some background.

Since this is client-side functionality, the actual sorting is done via JavaScript. Rather than re-inventing the wheel, we use jQuery and the tableSorter plug-in by Christian Bach (http://tablesorter.com/docs/index.html). This provides the basic functionality for sorting with a small footprint.

However this plug-in does not have the ability to “lock” rows together. In Christian’s defence, I’m not sure how it could be done in a generic way.

But, not all is lost. As well as providing the basic functionality the tableSorter plug-in also exposes an extension system to allow people to manipulate the grid after it has been sorted. This is what I used to “lock” the rows together.

The HTML

In order to make sense of the code below, here is an example of the HTML used to generate the above table:

<table class="SortableGrid" id="StatusGrid">
    <thead>
        <tr class="ProjectGridHeader">
            <!-- Header definition here -->
        </tr>
    </thead>
    <tbody>
        <tr id="projectData1">
            <!-- Project details definition here: CCNet -->
        </tr>
        <tr id="projectData2">
            <!-- Project details definition here: CCNet-Doc -->
        </tr>
        <tr class="buildStatus" id="link2">
            <!-- Build status definition here: CCNet-Doc -->
        </tr>
    </tbody>
    <tfoot>
        <tr>
            <!-- Footer definition here -->
        </tr>
    </tfoot>
</table>

I have omitted the details of each row – for the code below it is not required.

The important parts to note are:

  • Each <tr> in the <tbody> element has an id tag.
  • For project details rows the id starts with “projectData”
  • For build status rows the id starts with “link”
  • For linked rows the number in the id is the same (e.g. 2 in the example above)
  • All the build status rows have a class of “buildStatus”

This is all generated by the nVelocity template on the server.

Some jQuery Goodness

Now to lock the rows together, all we need is a bit of jQuery and some knowledge on the tableSorter plug-in.

To add an extension to the plug-in is a simple matter of adding a widget. This is done at a global level and then associated with the table when the tableSorter is initialised.

Here is the widget that I added:

$.tablesorter.addWidget({
    id: 'statusDisplay',
    format:function(table){
		$("tr.buildStatus",table.tBodies[0]).each(function(){
		    var row = $(this);

		    // Get the identifier of the owning row
		    var id = row.attr('id');
		    var linkedId = 'projectData' + id.substring(4);
		    var parent = $('#' + linkedId);

		    // Move the row to after the owning row
		    row.insertAfter(parent);
		});
    }
});

This selects gets all the rows with the “buildStatus” class and iterates through each row doing the following:

  1. gets the id of the row
  2. calculates the id of the linked row
  3. gets the linked row
  4. moves the status row underneath the details row (insertAfter will move the row)

All fairly simple with jQuery.

The final part is to associate the widget with the actual tableSorter instance. This is done when the tableSorter is initialised. I used the extensibility model of jQuery so I could separate the code from the template:

$.fn.initialiseProjectGrid = function(config){
    // Initialise the configuration
    var defaultConfig = {
        sortList: [[0,0]]
    };
    config = $.extend(defaultConfig, config || {});

    // Initialise the sorting
    this.tablesorter({
        sortList: config.sortList,
        widgets: ['statusDisplay']
    });

    // Allow chaining
    return this;
};

This ensures that the configuration is set, then initialises the tableSorter and associates the widget. To actually use the function is as simple as:

$('#StatusGrid').initialiseProjectGrid();

And that’s all there is to it.

In Summary

Here’s what the grid now looks like:

image

The status row is now underneath the project details where it should be. Sorting on the different columns will correctly keep the status row under the project it is for.

This also show how easy it is to extend the tableSorter plug-in to add some extra functionality. Thanks to Christian for making it so easy :-)

Posted in CruiseControl.Net | Tagged: , | Leave a Comment »

A Rant

Posted by Craig Sutherland on 27 January, 2010

This post is a rant about something that someone did recently to annoy me. There is no technical content, and it is only slightly related to CruiseControl.NET (CC.NET). So if you don’t care what annoyed, feel free to totally ignore this post!

And before I continue, I do realise that I am not perfect, I make just as many mistakes as the next person, and I know there are parts of the code that I have added that have bugs in it, so please do not take this post as saying that I am perfect.

So, what has annoyed me?

In the JIRA logs for CC.NET someone has raised an issue saying that security and sessions within the system are flaky. Flaky!

A “flaky” system is one that appears to work, but is broken to the point that it is almost unusable. In other words – it looks good, but is poorly developed!

Like I said above, I know there are some issues in my work, but describing the entire security as “flaky”? Every time I read that, I want to tell the person something rude (but I won’t, especially as I can’t tell him face to face!)

Why does this annoy me so much?

  1. The code for CC.NET is complex! When I first started on the project I spent several months digging through the code to figure out how things work (that was the original purpose of this blog).
  2. This was something no one else was willing to try (and I know why now!)
  3. Finally, I am not getting paid for this (at all!) I do this because I think it is a good product and I want to see it become even better.

So, reading that someone thinks that my work is so unstable to the point of being unusable makes me think “why do I bother?” Even more so considering I’ve been putting in a lot of effort trying to get 1.5 out the door.

So for the moment, I am ignoring that issue and waiting for it to drop off the “recently added” list. Unfortunately that means I probably won’t fix the issues (I’ll wait until someone who appreciates my work raises the issues) and it also means I’m not looking at the recently added list (at least until the issue drops off).

Anyway, rant over. I guess I should be thankful this is the first time I’ve had someone who doesn’t appreciate my work, but still wants me to fix things…

Posted in CruiseControl.Net | Tagged: | 3 Comments »

Automating the Documentation

Posted by Craig Sutherland on 21 January, 2010

Introduction

I have finally finished putting the last pieces in place for automating the documentation for CruiseControl.NET. This means that once Dave has put the pieces onto the server, the documentation will be automatically generated every time the code is updated :-)

This post is a quick summary of what is involved on the server side. The following posts describe what I have already built for the documentation generator:

And in this post I will describe how I added the tool to CruiseControl.NET.

Some Tidying Up

First off, I did some tidying up within the documentation generator. Previously it expected the arguments in a set order. Unfortunately this made it harder to integrate with CC.NET. Additionally, it did not generate any log file – it just displayed all the details to the console.

The first change was to use the OptionSet class from the Remote project (this actually came from Mono originally). This allowed me to quickly and easily add argument parsing to the tool. Now all of the arguments have a prefix and can be specified in any order.

For example, the generation command line went from:

Console.exe generate ..\..\..\..\..\project\Remote\bin\Debug\ThoughtWorks.CruiseControl.Remote.dll documentation

to

Console.exe -c=generate -s=..\..\..\..\..\project\Remote\bin\Debug\ThoughtWorks.CruiseControl.Remote.dll -o=documentation –xsd

While this is now a bit longer, it means the arguments can be specified in any order!

Second, I added a switch to generate a log file. When this switch is set the output will be sent to a log file (in an XML format) instead of the console. This log file also contains the type of information (debug, info, warning or error) plus the date and time.

With these changes, the tool is now ready to be integrated with the main build.

Preprocessing

Before the tool can be run there needs to be some pre-processing. First, the tool needs to be compiled – it is only included as source code in the repository. This is done using a NAnt build script and is very similar to how the main solution is built.

Additionally the NAnt script also does some clean up. This clean up involves deleting the previous log files. If this step is omitted then a failed build would get the logs from the previous successful build!

Finally the NAnt script moves the pages.xml file from the project trunk to the location where the tool expects it.

Now with all this done, the tool can generate and publish the documentation.

Calling the Tool

Calling the tool is really easy, just need to use the exec task. The following is how this task has been configured for generation:

<exec>
  <description>Generate the documentation for the Remote project</description>
  <executable>Tools\docGenerator\Console\bin\Debug\Console.exe</executable>
  <buildArgs>-c=generate -s=Build\Remote\ThoughtWorks.CruiseControl.Remote.dll -o=wiki -l=doc-remote.log</buildArgs>
</exec>

The baseDirectory setting has been omitted, so this will default to the project working directory (more on this below). This means all the paths are relative to the repository trunk.

The executable element tells CC.NET where to find the tool – since it has been compiled previously we tell it to look in the debug folder.

The buildArgs element is the arguments that I described above, except with the log file specified. The log file is being used so we can import the results into the build log.

And that’s all there is to it. The other exec tasks are similar, expect with different buildArgs. The only one that is very different is the publishing. This is because it requires a password and username and we don’t want everyone to see it! To get around this we added a batch file with the password and username in it, and all the rest of the arguments are passed though (I’ll write a post on this in future.)

The Final Touches

The final touches (or first depending on your thinking) is to add the project to the configuration. I decided to add this as a new project, so it can run independent of the main build.

The project has the same workingDirectory as the build project, but is in a different queue and has a different artefactDirectory (the different artefactDirectory is required to prevent cross-contamination of the build logs!) There is no source control block, as the code will have already been downloaded by the main project. It contains the NAnt script task and the four exec tasks. Finally, the publishers section merges the external logs and generates the build log (via xmlLogger).

And to trigger the project I added a forcebuild task to the main project. This will fire the documentation project when the build has finished (but before the publishers.)

And that’s all there is to it! In all, it took me a couple of hours to set this all up, but that also involved committing changes to the repository and generating entire builds of CC.NET (not a fast process!)

So, in the end, this is the completed project:

<project name="CCNet-Doc">
  <queue>Documentation</queue>
  <workingDirectory>e:\sourcecontrols\sourceforge\ccnet</workingDirectory>
  <artifactDirectory>e:\download-area\CCNet-Documentation\1.5.0</artifactDirectory>
  <tasks>
    <!-- Prepare for the documentation -->
    <nant>
      <description>Setup for documentation generation</description>
      <executable>Tools\NAnt\NAnt.exe</executable>
      <buildFile>docPrep.build</buildFile>
      <buildArgs>-listener:CCNetListener,CCNetListener</buildArgs>
      <targetList>
        <target>all</target>
      </targetList>
      <buildTimeoutSeconds>1000</buildTimeoutSeconds>
    </nant>

    <!-- Generate the documentation for the three projects -->
    <exec>
      <description>Generate the documentation for the Remote project</description>
      <executable>Tools\docGenerator\Console\bin\Debug\Console.exe</executable>
      <buildArgs>-c=generate -s=Build\Remote\ThoughtWorks.CruiseControl.Remote.dll -o=wiki -l=doc-remote.log</buildArgs>
    </exec>
    <exec>
      <description>Generate the documentation for the Core project</description>
      <executable>Tools\docGenerator\Console\bin\Debug\Console.exe</executable>
      <buildArgs>-c=generate -s=Build\Core\ThoughtWorks.CruiseControl.Core.dll -o=wiki -l=doc-core.log</buildArgs>
    </exec>
    <exec>
      <description>Generate the documentation for the WebDashboard project</description>
      <executable>Tools\docGenerator\Console\bin\Debug\Console.exe</executable>
      <buildArgs>-c=generate -s=Build\WebDashboard\ThoughtWorks.CruiseControl.WebDashboard.dll -o=wiki -l=doc-dashboard.log</buildArgs>
    </exec>
    <exec>
      <description>Publish the documentation</description>
      <executable>E:\tools\docGen.bat</executable>
      <buildArgs>-c=publish -o=http://confluence.public.thoughtworks.org/rpc/soap-axis/confluenceservice-v1 -s=wiki\pages.xml -l=doc-publish.log</buildArgs>
    </exec>
  </tasks>
  <publishers>
    <!-- Merge all the files -->
    <merge>
      <files>
        <file>doc-remote.log</file>
        <file>doc-core.log</file>
        <file>doc-dashboard.log</file>
        <file>doc-publish.log</file>
      </files>
    </merge>
    <xmllogger/>
  </publishers>
</project>

Hopefully this will be helpful to someone else as well :-)

Posted in CruiseControl.Net | Tagged: | Leave a Comment »

Beware Excessive Caching

Posted by Craig Sutherland on 20 January, 2010

Recently I added a few tweaks to CC.NET to try and reduce the memory usage (read the posts here and here). Unfortunately, this had the opposite affect to what I had hoped and instead increased memory usage!

The reason why is simple – I was too aggressive in my caching. I had added caches to both the dashboard and the server, and set the sliding expiry time to one hour. While this worked fine with smaller log files, it absolutely killed the dashboard for larger log files.

I tested on my machine with a 45Mb log file (big yes, but not outside the realm of possible). With the above settings I could view six log files – no more! Additionally, this locked the memory for an hour, both in the server and the dashboard (double memory usage – *ouch*)

Coming back to the original goal, it was to smooth out the peaks and dips in memory usage so it is a more even approach. So I’ve cut back on the amount of caching.

First, I reduce the amount of time log files were cached on the server. In my original scenario I was predicting that most people were interested in the most recent build, beyond that they were less interested. So I cut the caching time right back to five minutes (this is the default polling time within CC.NET for the interval trigger.)

And since I realise that I don’t know what other people have, I have made this a configurable setting. If your server has more memory, you can increase the cache time and hold more logs in memory. You can also reduce the time, but this can potentially lead to cache churn, so be careful with no going too low!

Second, I removed caching from the dashboard altogether. This is because the dashboard already caches the build pages, so this was leading to double caching again. Since people are most interested in the build pages rather than the log, I decided it was acceptable to remove the dashboard caching.

The result, I was now unable get out of memory exceptions with the 45Mb build logs on my machine (at least when viewing them in the dashboard). There will still be an absolute limit somewhere, but until we move to streams this is an ok solution.

Of course, to really fix the problem we will change to using streams, but there is no timeframe for these changes yet…

Posted in CruiseControl.Net | Tagged: , | Leave a Comment »

An Idea, but Not Good

Posted by Craig Sutherland on 19 January, 2010

An Anatomy of a Decision

In the week-end I had (what I thought was) a brilliant idea. Recently we modified the code to include the documentation, so we can have up-to-date documentation on the wiki. So, I thought why not add a task to CC.NET for generating and publishing documentation?

Initially this sounded like an easy task – after all, we already have a separate command-line tool for generating and publishing the documentation. However, as you might have guessed, by the end of the week-end I had decided against it.

Now, in case you are wondering, why am I writing this? First to make sure I don’t forget, second to see what other people think. So, here is the process I went through to reject the idea :)

First Planned Approach: Convert

My first approach was to take the current command-line tool and modify it so it would be a task within CC.NET. This would be the simplest approach and would be possible in a few hours. Additionally I could add an XSL-T for the dashboard and everything would look nice.

In the end, I rejected this approach for one simple reason – it would be specific to Confluence output only, and only if the codebase is using NetReflector.

So, rejected the approach, but as I thought more on it, I came up with a second idea.

Second Planned Approach: Extension Point

Next, I thought I could split the process into two. The first part would collect all the data about the code and collate it with the documentation. The second part would then be a formatter (perhaps XSL-T) to convert the data into the documentation.

Now, while this would be a total re-write of the documentation tool, I thought it would be helpful as people could then expand it to whatever documentation format they like.

But, trying to get the formatter working properly would require a lot of data – so what should be collected and what should be ignored? Second, how would this data be collected? The tool uses Reflection, but this loads the assemblies into memory and locks them on the disk!

After toying around with some ideas on how to get this working (Reflection only loads, separate AppDomain, etc.) I finally came to my solution.

Third and Final Approach: External Process

Finally I thought why not just run it as an external process? CC.NET can monitor it and import all the results as required.

Then the penny dropped – we already have this! The exec task and merge publisher has all the functionality in it already – no need to code anything! Additionally, the tool has already been written as a command-line application, so it will happily work without any changes!

In Conclusion

So, not every idea I have is a good one. Some on further thought are just re-inventing the way, and in a more painful way :-(

In this case we already had a perfectly good command-line application, plus the built-in tasks/publishers within CC.NET.

Sometimes the simplest solutions are the best…

Posted in CruiseControl.Net | Tagged: | 1 Comment »

CCValidator Improvements

Posted by Craig Sutherland on 11 January, 2010

I Need a Break!

For the past couple of months I’ve been reviewing documentation, fixing bugs, etc. – all in preparation for an RC1 release for 1.5. However, at times I need a break from the “hard slog” of software developer to play with some “fun” stuff.

And of course, I still have plenty of ideas to implement ;)

Configuration Hierarchies

One of the main issues that a lot of people have with CC.NET is its configuration. CC.NET is designed to be the ultimate in configuration flexibility, but this comes with a price – it is often more difficult to configure and diagnose problems. To help with this we added CCValidator to 1.4, and so far it sounds like it has been really helpful.

However for 1.5, the validator has been pretty much untouched (other than a few bug fixes), so I thought I’d add some funky new functionality to it. First on top of my list – alternate visualisations of the configuration.

For my first attempt, I decided to use the tried and true treeview to display the configuration, and so was born the “Hierarchy” view. Like its name suggests it uses a hierarchy to display the configuration.

How It Works

When CCValidator starts, you will see a new tab in the right-hand panel:

image

When a configuration file has been loaded, this tab gets populated:

image

At the root level there is a list of all the queues in the configuration. Expanding a queue node displays all the projects in that queue:

image

The number next to each queue is the number of projects in the queue – makes it easy to see which queues are loaded and which aren’t.

Clicking on an item (a project in the above example) displays the properties for the item. Some items also have additional sub-items, e.g. the project in the image above. This allows the user to quickly and easily drill down through the configuration.

A Work in Progress

Before everyone starts rushing off to JIRA to log faults with this new feature, let me tell you it is a work in progress! There are several known limitations:

  1. Not every node has drill-down functionality – I am still in the process of discovering what needs drill-downs and what doesn’t
  2. Not all the properties are displayed – this is because CC.NET uses a lot of fields internally instead of properties
  3. The property names are the internal names, not the configuration names
  4. The properties are editable, but nothing is saved – this is deliberate, later I will change them to read-only
  5. Some of the properties look really ugly – this is because I am using the raw data views, maybe in future I will fix this

Some of these issues I will fix, but others I will leave as they are for the moment. The reason why is very simple – I am trying not to modify any of the code in Core or Remote, only in Validator. So any changes that affect other projects will be dropped for the moment (this also means the code is a little hacky, in future I plan to remove these hacks and make the code of CC.NET more validator friendly!)

So, take a look at the new hierarchy and let me know what you think – any feedback is welcome :-)

Posted in CruiseControl.Net | Tagged: | Leave a Comment »

Documentation: Automated

Posted by Craig Sutherland on 9 January, 2010

Too Lazy

One of the things I dislike doing is more work than I need to. After all, with computers, we can automate all the tiresome repetitive tasks, so why do them manually?

For the configuration documentation in CruiseControl.NET, I wrote a simple documentation generator. This takes the DLL for a project, plus the XML doc-comment files and generates a set of .wiki files that contains the documentation:

image

This ensures that:

  1. The formatting is consistent
  2. Everything is covered
  3. The process is repeatable

While this is nice and easy, it stops short. The problem is these .wiki files are not where they need to be. People don’t look at these files, they look at the web site! So the process really needs to take these files (which are intermediate output) and publish them to the web site.

WSDL to the Rescue

Well, not really WSDL, but the web service interface on the Confluence web site. The Confluence Wiki has a Remote API that is exposed via a WSDL definition, so we can use that for updating pages in the wiki. SO the process now changes to:

image

I have used the same program, but changed it has it also has a publish mode. This publish mode takes one or more wiki pages and uploads it to the web site.

But there is also another file in the picture – Confluence Mappings – what does this do? The problem is not all the pages in the wiki have the same title as the configuration element – some are slightly different (don’t ask me why, just accept that they are!) The mappings file tells the publisher which wiki document is loaded to which page in the web site.

But Wait, There’s More

Just like the TV ads, this application has one more trick up its sleeve. We need to get the Confluence mappings from somewhere, and where better but the web site itself. So the program can download the list of all the pages and update the file:

image

This mode pulls the list of all the pages from the web site and stores them in the mappings file. Someone then needs to go through and manually add the mappings to the files (yes, it is not automated – yet!)

At the moment, the pages need to exist within the web site, if they don’t they cannot be added, this will still be a manual process. The same also happens for deleted pages – the file needs to be manually edited to delete the file.

So, this now provides an almost completely automated process for generating documentation and publishing it :-)

Some Nuts and Bolts

So, how do these processes work?

For the publishing the file is loaded and parsed into an XDocument. The process then goes through and do the following:

  1. Check for a mapped file
  2. Make sure the file exists
  3. Downloads the page from the wiki
  4. Compares for differences
  5. Upload the file

If any of the steps fail (e.g. no file, no page, no differences), the item is skipped and the next item is handled. As you can imagine, because each page is downloaded this process can be a bit slow, especially over a slow connection, but it is still faster than a manual process (especially when a lot of pages have been changed!)

The list is even simpler, it downloads the list of pages and adds any new pages to the file. Because this is a single API call it is nice and fast!

Where To From Here?

As you can imagine, this utility is still pretty basic, although it has grown more than I expected! We will keep refining the documentation being generated, and maybe even look at speeding up the publishing, but in general I am not planning any more big changes to this tool.

Instead, I would like to look at adding this to the build process, as this would ensure that the documentation is always up-to-date. As such, we would probably need to tidy up the reporting a bit to make it easier to integrate into CC.NET. But this would certainly make the documentation both easier to publish and more up-to-date :-)

What do you think?

Posted in CruiseControl.Net | Tagged: | 4 Comments »

Countdown to 1.5

Posted by Craig Sutherland on 7 January, 2010

I have had a chat with some of the other devs on the project and we are going to try and get the 1.5 release out sometime soon.

This means we will no longer accept patches for enhancements or new functionality, although we will still accept patches for bug fixes! For the next month we will be focusing on resolving issues within the 1.5 release.

At the moment I am reviewing the issues list within JIRA to see which issues can be resolved. I’ve gone right back to the beginning to see if there are any issues that have been resolved with prior fixes, so it is slow going. Hopefully in the next week or so I will be up to the more recent issues, which I will then try to resolve.

So, if you find any problems or issues with CruiseControl.NET, please log them in JIRA (http://jira.public.thoughtworks.org/browse/CCNET). Do not use the comments within Confluence, as will not be looking at them for a while (actually, do not use comments in Confluence for logging issues, other than issues with the documentation!) The Google Groups can be used for discussing potential issues, but given the volume of e-mails I get it is easy to lose e-mails, so JIRA is the best place to log them for resolution.

So, what is our timeline? Basically we are planning to do an RC1 release this month. If there are no major issues within the next couple of months, we will do the “official” release, otherwise we’ll do another RC release and wait for a couple more months.

So, not long to go now :-)

Note: After the 1.5 release we are looking at doing a 1.6 release. This will mainly be a tidy up release, plus any enhancements or new functionality that has been deferred. The changes to use streams instead of strings will not be until a 2.0 release, as they are pretty major! Hopefully we will be able to get 1.6 out pretty quickly this year and then focus on the major refactorings for 2.0

Posted in CruiseControl.Net | Tagged: | 4 Comments »

Documentation: Server Configuration Completed

Posted by Craig Sutherland on 17 December, 2009

I have now finished migrating the configuration documentation for the server into the code base. This means all the values for the server configuration are documented (although sometimes not well). This also included adding any missing documentation and ensuring everything is in a consistent format.

So take a look and leave a comment in the documentation if you notice anything that should be corrected.

Note: Do NOT leave comments about bugs or issues in the documentation! We do not read these very often. If you need to log a bug, please use either the mailing lists (see http://confluence.public.thoughtworks.org/display/CCNET/Mailing+Lists) or JIRA (see http://confluence.public.thoughtworks.org/display/CCNET/Issues).

And yes, I know people will ignore the note, deleting comments gives me something nice and easy to do :-)

Posted in CruiseControl.Net | Tagged: | 1 Comment »

Parlez-vous Français

Posted by Craig Sutherland on 16 December, 2009

A Long Time Ago…

Back in the 1.4 days, when we were still investigating what to put into 1.5, I looked into adding multi-lingual support to CruiseControl.NET. Basically I had hoped to make CruiseControl.NET available to more people, especially those who did not speak English (or for whom English was not their first language).

As nice as the idea was, there was one major snag – no translators :-( So after a while the idea died a death. Unfortunately, my linguist ability is extremely limited (I’m learning Chinese, but it’s a painful and slow process!) So when we started preparing for the 1.5 release, the multi-lingual changes were dropped.

Recently, one of our users asked how to switch languages in the dashboard. When we told him it couldn’t be done, he came back with an offer of translating into French for us! So I have dusted off some of the old archived code and re-added multi-languages to CruiseControl.NET.

Keeping It Simple

Now the original version of the multi-lingual changes started with the Remote project and worked their way up through Core, Server and then onto the Dashboard (CCTray was bypassed). This meant, in theory, an administrator could completely switch their CC.NET install into a different language.

While this was a grand vision, it had a few detractors. For one, should exceptions be translated? (I said yes, others said no.) What about documentation? (Especially considering our documentation needs some TLC anyway.) What about support? Testing? Etc.

So for this second attempt I have pared it right back. The only thing that is translated is the dashboard – everything to do with the actual CI server is untouched. This, hopefully, bypasses most of the above concerns, while still allowing non-English speakers to use the system.

Additionally, I changed the code slightly. Originally the language was hard-coded – everyone had to use the selected language. The new version uses the language detection in ASP.NET and selects the user’s primary language (as specified by their browser).

Now for the bad news – currently there is only one translation, French, and it is still a work in progress. So, if you speak French, here is what the dashboard looks like currently:

image

And just to show that it is different, if I change my language back to English:

image

Acknowledgements

As I mentioned earlier my linguist ability is virtually nil. Louis-Philippe Carignan is the kind volunteer who provided the French translation. Many thanks Louis-Philippe, I hope this helps you :-)

Posted in CruiseControl.Net | Tagged: | 7 Comments »