Creating a K2 Deployment Package from code

In our current project, we are using Tean Foundation Server to automate our build process. This also means MSI’s are created for every artifact to deploy, eventually using TFS’s functionality to deploy a new msi when the build quality is changed. MSI’s are build in a way so they can be installed from command line without user actions required (unattended installation). So, when we change the build quality of a build it can fully automatically be deployed to a testing environment.

Most of our software that needs to be deployed are webservices, websites or web parts. K2 however, is somewhat harder to deploy. Since BlackPearl it has become a lot easier because you can use a MSBUILD command to deploy a process. For those of you who don’t know about this. Check out KB000188 at the K2 Knowledge base. In general, it comes down to this command:

MSBUILD DeploymentPackage.msbuild /p: Environment=Development

It’s easy to create a MSI or (PowerShell) script that starts the MSBUILD command and installs the process. But how do we automate the creation of the deployment package? Since this is normally done from VS2005.

Building/deploying using code

The solution is actually quite easy, but we’ll get to that later. First off all k2 developers should have a copy of the developer reference. This documentation is still work in progress but there is a lot of information already in there. It also has an example on how to deploy a process using code. This utilizes the DeploymentPackage and Sourcecode.Framework.ProjectSystem.Project class. So the msbuild command can actually be replaced with a custom-coded application, which is even nicer for a msi than just kicking of a msbuild command.
The deploymentPackage is produced using the Project.CreateDeploymentPackage(). From the function name you’d expect it to do the same as the VS2005 context menu. It however does not.

Creating the deployment package

So if the CreateDeploymentPackage() doesn’t create the deployemtn package, what does? Well, simple, the Save() method of the Project. It takes in two parameters. The directory where to save the deployment package and the name of the deployment package.
The result is the same file/folder structure as it is described in the KB000188 article.

Great! Now why is this article longer?

Well, from what I’ve found, the code in the developer reference does not export the environment variables and so the Project.Save() method does not give you any environment information. This also results in a empty stringtables section when you use the Developer reference example code.

From what I can find out, the environment settings need to be added to the package itself.
Since the K2 api is so nice to code agains, this results in the following method:

private void AddEnvironment(DeploymentPackage package, string SelectedEnvironment) {
  EnvironmentSettingsManager envManager = new EnvironmentSettingsManager(true);
  envManager.ConnectToServer(this.K2ConnectionString);
  envManager.InitializeSettingsManager(true);
  envManager.Refresh();

  //Add environments + environment properties.
  foreach (EnvironmentTemplate envTemp in envManager.EnvironmentTemplates)
  {
    foreach (EnvironmentInstance envInst in envTemp.Environments)
    {
      //Add an environment to the package.
      DeploymentEnvironment env = package.AddEnvironment(envInst.EnvironmentName);
      foreach (EnvironmentField field in envInst.EnvironmentFields)
      {
        env.Properties[field.FieldName] = field.Value;
      }

      // Make sure the environment we select actually exists.
      if (envInst.EnvironmentName == SelectedEnvironment)
      {
        package.SelectedEnvironment = envInst.EnvironmentName;
      }
    }
  }

  //Cleanup
  envManager.Disconnect();
  envManager = null;

  //Final check of the selected environment
  if (package.SelectedEnvironment == null || package.SelectedEnvironment == string.Empty)
  {
    throw new Exception(string.Format("Could not find the environment you wanted to select ({0})", SelectedEnvironment));
  }
}

The above method adds the environment fields to the DeploymentPackage. The DeploymentPackage is part of the Project and if we deploy the project now, the environment settings are exported to the server. This also means when we call the .Save() method, the environment settings are stored inside the .msbuild script that the .Save() method produces.
The environment manager can be located in the SourceCode.EnvironmentSettings.Client assembly and the ConnectionString used is retrieved from the SourceCode.Hosting.Client.BaseAPI.SCConnectionStringBuilder object.

And voila, a deployment package is created using code!

Download!

I’m still working on a final version that creates the deployment package using a MS build task, this MSBuild task will be added to the TFS build so the TFS build on the build server creates the deployment packages. This is done by inheriting from the Task object.
The attached file is a example on how to do that. The code works, but i’m still having small issues to getting it work on the TFS server itself. Nevertheless, you can use the class to create your own command line tool to deploy the process.