Monday, September 28, 2015

Automatic update of assembly version using TFS2013

To successfully implement Octopus Deploy you need to have unique version numbers for each build. If you don't want to manually edit the assembly info this could be a real pain in the ***. With the following trick you can automatically generate version numbers using TFS Build server 2013.

What I did was create a BuildCommon.targets that automatically searches for the AssemblyInfo and updates the version number that matches the build number as generated by TFS, and check this file in your codetree. In our case the file is named: BuildCommon.targets and is placed next to the root of the solution:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">

<!--
    Defining custom Targets to execute before project compilation starts.
-->
<PropertyGroup>
    <CompileDependsOn>
        CommonBuildDefineModifiedAssemblyVersion;
        $(CompileDependsOn);
    </CompileDependsOn>
</PropertyGroup>

<!--
    Creates modified version of AssemblyInfo.cs, replaces [AssemblyVersion] attribute with the one specifying actual build version (from MSBuild properties), and includes that file instead of the original AssemblyInfo.cs in the compilation.

    Works with both, .cs and .vb version of the AssemblyInfo file, meaning it supports C# and VB.Net projects simultaneously.
-->
<Target Name="CommonBuildDefineModifiedAssemblyVersion" Condition="'$(VersionAssembly)' != ''">
    <!-- Find AssemblyInfo.cs or AssemblyInfo.vb in the "Compile" Items. Remove it from "Compile" Items because we will use a modified version instead. -->
    <PropertyGroup>
        <VersionAssembly>$([System.Text.RegularExpressions.Regex]::Replace($(VersionAssembly), `[\w|\D]+_`, ``, System.Text.RegularExpressions.RegexOptions.IgnoreCase))</VersionAssembly>
    </PropertyGroup>
    <ItemGroup>
        <OriginalAssemblyInfo Include="@(Compile)" Condition="(%(Filename) == 'AssemblyInfo') And (%(Extension) == '.vb' Or %(Extension) == '.cs')" />
        <Compile Remove="**/AssemblyInfo.vb" />
        <Compile Remove="**/AssemblyInfo.cs" />
    </ItemGroup>
    <!-- Copy the original AssemblyInfo.cs/.vb to obj\ folder, i.e. $(IntermediateOutputPath). The copied filepath is saved into @(ModifiedAssemblyInfo) Item. -->
    <Copy SourceFiles="@(OriginalAssemblyInfo)"
          DestinationFiles="@(OriginalAssemblyInfo->'$(IntermediateOutputPath)%(Identity)')">
        <Output TaskParameter="DestinationFiles" ItemName="ModifiedAssemblyInfo"/>
    </Copy>
    <!-- Replace the version bit (in AssemblyVersion and AssemblyFileVersion attributes) using regular expression. Use the defined property: $(VersionAssembly). -->
    <Message Text="Setting AssemblyVersion to $(VersionAssembly)" />
    <RegexUpdateFile Files="@(ModifiedAssemblyInfo)"
                Regex="Version\(&quot;(\d+)\.(\d+)(\.(\d+)\.(\d+)|\.*)&quot;\)"
                ReplacementText="Version(&quot;$(VersionAssembly)&quot;)"
                />
    <!-- Include the modified AssemblyInfo.cs/.vb file in "Compile" items (instead of the original). -->
    <ItemGroup>
        <Compile Include="@(ModifiedAssemblyInfo)" />
    </ItemGroup>
</Target>

<UsingTask TaskName="RegexUpdateFile" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
    <ParameterGroup>
        <Files ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
        <Regex ParameterType="System.String" Required="true" />
        <ReplacementText ParameterType="System.String" Required="true" />
    </ParameterGroup>
    <Task>
        <Reference Include="System.Core" />
        <Using Namespace="System" />
        <Using Namespace="System.IO" />
        <Using Namespace="System.Text.RegularExpressions" />
        <Using Namespace="Microsoft.Build.Framework" />
        <Using Namespace="Microsoft.Build.Utilities" />
        <Code Type="Fragment" Language="cs">
            <![CDATA[
            try {
                var rx = new System.Text.RegularExpressions.Regex(this.Regex);
                for (int i = 0; i < Files.Length; ++i)
                {
                    var path = Files[i].GetMetadata("FullPath");
                    if (!File.Exists(path)) continue;

                    var txt = File.ReadAllText(path);
                    txt = rx.Replace(txt, this.ReplacementText);
                    File.WriteAllText(path, txt);
                }
                return true;
            }
            catch (Exception ex) {
                Log.LogErrorFromException(ex);
                return false;
            }
        ]]>
        </Code>
    </Task>
</UsingTask>

</Project>

Then change the build number format to:
$(BuildDefinitionName)_0.1.$(Year:yy)$(DayOfYear)$(Rev:.r)

and the MSBuild arguments:
/p:CustomAfterMicrosoftCommonTargets="$(TF_BUILD_SOURCESDIRECTORY)\src\BuildCommon.targets" /p:RunOctoPack=true /p:OctoPackPublishApiKey=API-123465 /p:OctoPackPublishPackageToHttp=http://octopus-server/nuget/packages /p:VersionAssembly=$(TF_BUILD_BUILDNUMBER)

This should result in unique assembly versions for each build.

Many thanks for the creators of these posts to help me create this:
http://www.lionhack.com/2014/02/13/msbuild-override-assembly-version/
http://blog.casavian.eu/blog/2014/04/23/increment-version-for-changed-assemblies-only-first-part/
http://blogs.msdn.com/b/visualstudio/archive/2010/04/02/msbuild-property-functions.aspx

Cheers,
Luuk

Tuesday, September 15, 2015

Install Sentry (an open source error logger) on Azure using Docker containers

Start with an Azure VM: Docker on Ubuntu Server (create one on http://portal.azure.com) Create an account at http://hub.docker.com so you can pull containers. Now, when the VM is fully loaded, login with putty or any other ssh client to your Azure vm and type the following commands:
$ docker login
$ docker search redis ### (optional search for redis)
$ docker pull redis
$ docker pull postgres
$ docker pull sentry
$ docker run -d --name sentry-redis redis
$ docker run -d --name sentry-postgres -e POSTGRES_PASSWORD=yourpassword -e POSTGRES_USER=sentry postgres
$ docker run -d --name sentry -p 8080:9000 --link sentry-redis:redis --link sentry-postgres:postgres sentry
$ docker run -it --rm --link sentry-postgres:postgres --link sentry-redis:redis sentry sentry upgrade
$ docker run -d --name sentry-celery1 --link sentry-redis:redis --link sentry-postgres:postgres sentry sentry celery worker
$ docker run -d --name sentry-celery-beat --link sentry-redis:redis --link sentry-postgres:postgres sentry sentry celery beat
For me, the initial user didn't have enough rights, so I created an additional user using:
$ docker run -it --rm --link sentry-redis:redis --link sentry-postgres:postgres sentry sentry createsuperuser
To make the web portal accessible, you'll have to open the port Azure, using Settings - Endpoints:

All kudo's for this post go to: https://hub.docker.com/_/sentry/ for the excellent description, I've only created this post to add the additional docker pull / Azure stuff. I don't know if this is how you want to run it on production, but at least you have a very easy test environment.

Now you can compare this with other error loggers like:

What is your experience with error loggers and monitoring tools and which one would you recommend?

Cheers,
Luuk

Friday, July 3, 2015

Unhandled Exception: System.InvalidOperationException: Cannot dispose the build manager because it is not idle.

Today we got this really annoying error when building on TFS2010:


Long story short: Not our build server was causing this error, but the TFS server itself... it was out of diskspace..

So please check this first before blaming everything else except TFS :)

Cheers,
Luuk

Wednesday, March 4, 2015

What I've learned from reading RESTful Web APIs

I've recently finished reading the book RESTful Web APIs by Leonard Richardson, Mike Amundsen, Sam Ruby.


Wish I've read this book before building an API. To summarize the things I would have done different (and you've should have done probably too):

  • Use standard naming conventions for properties from for example: http://schema.org/docs/schemas.html
  • Don't use application/json but a custom format like application/vnd.sameproblemmorecode.blog+json
  • Make better use of the default HTTP Headers (e.g. the WWW-Authenticate and Link header)
  • Return errors as described in https://tools.ietf.org/html/draft-nottingham-http-problem-06
  • Create hypermedia links in the HTTP headers to describe possible links. These links should also have standartized names from for example http://www.iana.org/assignments/link-relations/link-relations.xhtml
  • If time allows; event create hypermedia profiles (this allows the server to change without breaking clients). One of the writers is also writing a book on how to create hypermedia driven clients for this.
  • Make sure to reuse as much standards as possible, we don't need another new standard. This enables us to reuse webcomponents (or at least parts of) between projects.
Hope this helps.

Cheers,
Luuk

Sunday, November 2, 2014

My Development Setup

Last week my PC got upgraded. This blogpost serves as a reference for all the stuff I do to personalize Visual Studio and Fiddler.

First thing I do is disable 'Automatically adjust visual experience based on client performance' and 'Enable rich client visual experience', but keep 'Use hardware graphics acceleration if available' enabled. Speed is everything baby!

Then I customize the toolbar and add BC. Pro-tip; remove all toolbar button's you never use.

I always install the following plugins:

Setup the Rename Visual Studio Window Title plugin (to see which branch I'm working in):

Setup Scrum Power Tools (I use this for code reviews and workitem shortcuts in the toolbar):
  1. Assign the work item and backlog items to Shortcut #1 and Shortcut #2
  2. Customize the toolbar and add the button for Shortcut #1 and Shortcut #2 to the standard toolbar

This is what my final toolbar looks like:


I also use fiddler for API debugging. For API's its really important to see the HTTP method. To add this column, enable / add the following block in the Rules > Customize rules file:
public static BindUIColumn("Method", 60)
function FillMethodColumn(oS: Session): String {
   return oS.RequestMethod;
}

When you also retrieve large binary blocks, fiddler can really slow down when you accidentically click on one. The very powerfull Customize rules file, also has a solution for this. Add the following code inside the OnPeekAtResponseHeadersfunction. This will drop large response bodies, which slows down fiddler.
// This block enables streaming for files larger than 5mb
if (oSession.oResponse.headers.Exists("Content-Length"))
{
  var sLen = oSession.oResponse["Content-Length"];
  var iLen: Int32 = 0;
  if (!isNaN(sLen)){
    iLen = parseInt(sLen);
    if (iLen > 5120000) {
      oSession.bBufferResponse = false;
      oSession["ui-color"] = "brown";
      oSession["log-drop-response-body"] = "save memory";
    }
  }
}

Monday, September 22, 2014

Major update for the SBQueueManager

Also having problems managing the service bus for windows server?
With the latest update of the SBQueueManager you can handle it all.

The improvements include:

  • Topic support
  • Subscription support
  • New user right to manage queues and topics
  • Update queue and topic support
  • Less crashes
  • More feedback
Check it out!


And offcourse: source is available at Github: https://github.com/luuksommers/SBQueueManager

Happy managing!
Luuk

Tuesday, July 8, 2014

Automatically deploy services using TFS2010

All credits for this post goes out to: Hrusikesh Panda and Mike Hadlow (see references below).

To deploy Windows services with TFS2010 there are 2 major challenges; seperate the binaries on a per-project basis and automatically stop and start a service without changing the build workflow. Sounds hard? It isn't!

If you don't want any blabla and download the sample solution directly, goto github.

First add the following files to your Solution folder and make sure they will be committed to TFS:
DeployApplication.targets (this is a copy of the WebDeploy targets from Microsoft with an extra change to copy the .config file).
DeployService.targets (this stops the service, copies the files and starts the service, it also contains the location on where to put the files)
safeServiceStart.bat (helper to start a remote service)
safeServiceStop.bat (helper to stop a remote service)
you can simply add these files to the solution items (right click solution and click add existing item).

Edit the DeployService.targets for the right paths on the remote machine. The directory where the service is located should be available for the user running the build using a standard windows share (\\<servername>\<project directory>\<servicename>, the servername is determined during the build and can be configured for each build quality.

Copy the Deploy.targets into the project you want to deploy, unload the project file and edit it with visual studio. Lookup the following line:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
And add this line below:
<Import Project="Deploy.targets" Condition="'$(DeployOnBuild)'=='True'" />
Save the file and reload the project

Once you've reloaded the project, edit the Deploy.targets (just double click on it) and change the service name and service location. Commit all changes to TFS.

The next step is to install the service on the remote machine. (using installutil, or whatever method you like, in the sample we can install a service by running it with the -I command line argument). The automated deployment will only start and stop the service and is not able to install and delete the service. (In my case, the service is running as a domain user, and therefor I prefer to pre-install the service with the right credentials or else you'll have to enter the credentials in the deployment script).

To enable automatic deployment, create a new Build Configuration on your TFS2010 server and set it up with a scheduled trigger (or whatever trigger you want). In the process section add the following MSBuild arguments:
 /p:DeployOnBuild=True /p:DeploymentServerName=\\<server-to-deploy-to>
If you also want to configure transformations (change the .config file during deployment), have a look at SlowCheetah. This works without a problem with this configuration. To make sure SlowCheetah only works during deployment, I've edited the project file again and added AND '$(DeployOnBuild)'=='True' in the following line:
<Import Project="$(SlowCheetahTargets)" Condition="Exists('$(SlowCheetahTargets)') AND '$(DeployOnBuild)'=='True'" Label="SlowCheetah" />

(If you don't see this line, make sure you've installed SlowCheetah extension and added the transformations by right click on a .config file and click add transformations)

References:
http://mrchief.calepin.co/deploying-windows-service-via-msbuild
http://mikehadlow.blogspot.nl/2009/06/tfs-build-publishedwebsites-for-exe-and.html
https://github.com/luuksommers/TFSDeployedService

Happy deploying!

Luuk