Archive for the ‘Build Server’ Category

We already compile, run tests and package the result as part of our continuous integration process. Now we want to go a step further and deploy to a test server if there are no errors.

At a high level the steps involved are:

  1. compile
  2. run tests
  3. package
  4. copy to destination
  5. deploy/install

We use TeamCity for continuous integration and Ruby/rake to build, test and package. We’d really like to use the same tools for deployment. We looked around for an existing tool in Ruby and found capistrano but quickly learned that it doesn’t work isn’t supported on Windows and requires life support services like ssh to be running. We didn’t find another tool so we started thinking about how to break the problem down and solve it ourselves. We came up with two required capabilities: the ability to run a command on a remote (deployment) server, and the ability to install our software. There are a number of ways to solve these problems. We used the one described below because it simply automated our current manual deployment process without introducing much complexity (like directory watching services, etc). We started with the install piece since it was the “known”.

First we copy the file archive from the build server. Copying involves finding the most recently created file in the build archive folder.

buildPath = '\\\\rivendell\c$\Projects\dist'
archive = most_recent_file(buildPath)

We sort the files in the build archive folder by the date they were created and return the path of the most recent one.

def most_recent_file(path)
  entries = Dir.entries(path).sort { |file1,file2| File.ctime(File.join(path, file1)) <=> File.ctime(File.join(path, file2)) }
  files = File.join(path, entries.last)

Yes, we realize it would be more efficient to add the paths first, collect the dates into objects, sort the objects… etc. We’re still learning Ruby so we kept it simple.

To eliminate visual clutter in path joining we add a / operator to string.

class String
  def /(other)
	File.join(self, other)

resulting in…

def most_recent_file(path)
  entries = Dir.entries(path).sort { |file1,file2| File.ctime(path / file1) <=> File.ctime(path / file2) }
  files = path / entries.last

Next we get just the file name from the returned archive path as we need that for deployment.

filename = File.basename(archive)

Now handle the case where we don’t have any archives on the build server.

if (filename == '.')
  puts "No archive to deploy"

If we found an archive copy it unless we already have it.

if (!File.exists?(filename))
  puts "Copying #{archive}"
  FileUtils.copy(archive, '.')

Now, whether we already had it or not, deploy the archive. In the manual use case we may already have copied it and are just re-deploying. We remove the old directory recursively then unzip the new archive to the same location.

puts "Deploying #{filename}"
sh "\"c:\\Program Files\\7-Zip\\7z.exe\" x -y #{filename}"

We’re assuming nth deployment here, not first deployment. IIS has already been setup to point to Tool.Web etc.

So we have a simple Ruby script, really a rake task, that we can run on the test server to fetch and install the latest code. Next we have to figure out a way to trigger rake on the test box from the build box. We want something like the unix rsh command and a short conversation with one of our IT folks introduces us to PSExec, a part of PSTools.

The gui version of PSTools was extremely useful in trying out various command variations for triggering our rake task on the deployment server.

The command that worked from the gui was:

psexec.exe \\moria -u "lotr\gimli" -p "gold" -e "cmd.exe" /K "cd C:\tools && c:\Ruby\bin\ruby.exe c:\Ruby\bin\rake deploy" && exit

One problem we encountered was when we ran the build it would deploy successfully but sh would never return and as a result the build would time out. We added a 60 second timeout “-n 60” to the psexec call but the build still timed out.

psexec.exe \\moria -u "lotr\gimli" -p "gold" -n 60 -e "cmd.exe" /K "cd C:\tools && c:\Ruby\bin\ruby.exe c:\Ruby\bin\rake deploy" && exit

When we tried the -d switch (don’t wait for app to terminate) the deployment was successful but the build would fail because psexec returns the PID number for the process on the remote machine instead of the expected success value 0.

psexec.exe \\moria -u "lotr\gimli" -p "gold" -n 60 -e -d "cmd.exe" /K "cd C:\tools && c:\Ruby\bin\ruby.exe c:\Ruby\bin\rake deploy" && exit

After much experimentation we decided to keep the -d and wrap the sh call in a try…rescue. That resolved both the timeout and the unexpected failure at the expense of possibly masking a real deployment failure.

Here’s the final deploy target in our build server rakefile:

task :deploy => [:dist] do
  @cmd = "c:\\pstools\\psexec.exe \\\\moria -u \"lotr\\gimli\" -p \"gold\" -n 60 -e -d \"cmd.exe\" /K \"cd C:\\tools && c:\\Ruby\\bin\\ruby.exe c:\\Ruby\\bin\\rake deploy && exit\" && exit"
    sh @cmd
  puts "deploy succeeded"

Read Full Post »

Login/remote to your build server. Use the login under which you want the build agents to run.

If you are using TeamCity 5.0 and above the Git client is included with the install, you can skip to Configuration.

Download the Git client.

Copy the zip file to your .BuildServer/plugins directory

Stop and start the JetBrains Web Server service so that it detects the Git plugin.


Connect to your TeamCity website and go to the Projects page.

Select the project you want to configure to use Git and go to its Version control settings page.

Click to Edit the version control configuration.

Copy the clone url from your repository (e.g. GitHub).

Paste the clone url in to the TeamCity form.

Enter a directory name where Git should manage its files for this repository.

Enter the user name from your clone string.

Enter the location of your private ssh key.

Verify that the ssh key directory exists. If not, generate the key from a git bash shell:

Verify that the public key for this user is associated with your repository.

Open services and change your build agents to run under a local account.

Test the connection


Now do a test build on your project.

Read Full Post »

If you get an error like the following on your build server:

error MSB4019: The imported project “C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets” was not found. Confirm that the path in the declaration is correct, and that the file exists on disk.

You have two options. You could copy Microsoft.WebApplication.targets from your development box, or you could install Visual Studio on the build server. The simplest solution is to copy the file, making sure to reproduce the directory structure exactly.

Read Full Post »

How to get multiple TeamCity build agents running on one server.

First log in to the server where you want the agents to run then open TeamCity from a browser on that box.

Go to the Agents tab.

From the top right of the page choose Install Build Agents then MS Windows Installer.

When prompted choose to run the agent installer. You may have to be an administrator since this will be installing windows services.

Choose the directory where you want the agent configuration and working directories to live. I put them under the TeamCity home directory.

Take the defaults as you work your way through the installer.

The agent directory is being configured.

Here it is important that you choose a unique name and port. The directories should be consistent with your previous choices. You may need to change the server URL and port.

When this popup appears do not click OK yet.

Open an Explorer window and navigate the the launcher/conf directory under the build agent directory you configured above.

Edit wrapper.conf and change the name of the ntservice to match your agent name as appropriate. This is important because each agent runs under a different service and they must have unique names, otherwise only one will connect at a time.

Now you can click OK on the popup.

And choose the defaults the rest of the way through the installer.

If you open Services

You should see the service you named.

The new agent should connect and be visible from the TeamCity Agents page within a few moments.

Repeat the steps to add additional agents.

Read Full Post »

%d bloggers like this: