Showing posts with label TFS. Show all posts
Showing posts with label TFS. Show all posts

Tuesday, April 12, 2016

Release Management in TFS 2015.2

With the TFS 2015.2 update we now have the ability to use Release Manager in TFS on premise. This is how I have setup our branching structure, gated check-in and release manager to control the deployment (with approvals at some stages) of an MVC5 web site with Entity Framework into multiple Azure Web Application environments.

We have our branching structure set as:

Dev\developer name 1

Dev\developer name 2 etc

Main

Developers take a branch from main into the dev folder under their name and work on changes. When development is complete they check-in to their dev branch and then merge the changes into the Main branch. This allows the developer to pull in other changes from main into their dev branch, and be able to check-in at least daily into their dev branch so their code is backed up overnight.

We are using the new TFS build tasks; and setup against the Main branch is:

  • A gated check-in.

image001_thumb7_thumb

  • And a number of build tasks that compile the code in Release mode, run some unit tests and finally copy the files/build artifacts we need to deploy the web site into the drop folder (essentially a web deploy zip file is created).

image002_thumb2_thumb

 

The key to creating a web deploy zip file is the MS Build Arguments:

/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true

In the copy and Publish build artifacts task we have:

image003_thumb1_thumb

This automatically builds all changes checked into the Main branch with the final step it produces the web deploy files. Assuming the build passes then Release Manager will take over to deploy the web site into an Azure Web Application.

We have 4 environments in Azure that we need to deploy to ‘UAT Staging’, ‘UAT’, ‘Production Staging’ and ‘Production’. For us these are split into 2 Azure subscriptions, one for UAT one for Production. Within each subscription is 1 web application e.g. UAT and against that 1 slot ‘staging’ and each has its own Azure SQL database. This gives us 2 websites and 2 databases. We make use of sticky slot settings as well so the connection string / app settings stay against its environment because in production we make use of the Azure web application swap slot functionality.

So we configure release manager to deploy the code to UAT staging, UAT and Production staging. But for production release manager just initiates a swap slot PowerShell command.

So in Release manager we will need to setup the deployment process or tasks for each of these environments.

But first Release manager is setup to watch the main branch for a new build artifact which is done by setting the release trigger to Continuous Deployment.

image004_thumb1_thumb

As soon as a new version is checked in setting the UAT Staging environment trigger to ‘Automated after release creation’ will initiate a deployment into that environment automatically (and for us with no approvals required because that is the first server in Azure which our developers can test against).

On the environments tab we define the deployment steps for each environment so for UAT Staging we have:

  • Stop the Azure web application
  • Deploy the new code/web site
  • Start the Azure web application

image005_thumb1_thumb

We are making use of the new TFS Market Place extension ‘Run Inline Azure Powershell’ task which allows us to stop the Azure web application with:

Stop-AzureWebsite -Name $(WebAppName) -Slot stage

And below are the properties of the Azure Web App Deployment task, with the main one being the path to the Web Deploy Package.

Note: Our web applications are always running in Azure we are not creating them on demand.

image006_thumb2_thumb

The UAT and Production Staging environments all have the same 3 tasks. Deployment into an environment takes about 1 minute 20 seconds.

The Production environment has 1 task which swaps Production Staging with Production by executing this powershell script:

Switch-AzureWebsiteSlot -Name $(WebAppName) -Slot1 stage -Slot2 production -Force -Verbose

image007_thumb2_thumb

That allows us to do a quick production deployment (saving us that 1 minute 20).

UAT, Production Staging and Production all have Pre-Deployment approvers setup.

image008_thumb1_thumb

So deployment to the 1st Azure environment ‘UAT Staging’ happens automatically upon a successful check-in to the Main branch.

The developer has the chance now to manually test the site. When they want to deploy to the next environment ‘UAT’ they would open the release in release manager and start the deployment:

image009_thumb1_thumb

image010_thumb1_thumb

By using the ‘Deploy’ button to request the code is deployed into the UAT environment this will send an email to the approvers who will ‘hopefully’ approve the release and if they do release manager will execute the 3 deployment tasks setup for the UAT environment.

The same process would be followed for the remaining environments, the developer tests then uses the deploy button to move the same web application code to the next environment.

At any point we can check what release is in what environment by looking at the overview tab.

image011_thumb2_thumb

Database changes are done via Entity Framework Code First Migrations which are executed upon web site start up by running this code added to the MVC site startup.cs class:

var efConfiguration = new Configuration();

var dbMigrator = new System.Data.Entity.Migrations.DbMigrator(efConfiguration);

dbMigrator.Update();

This will execute any schema changes and then run the seed data method. The key to keeping the seed data updated is using the extension method ‘AddOrUpdate’.

 

Useful Links

https://msdn.microsoft.com/library/vs/alm/release/overview

Sunday, August 30, 2015

Unit Testing (part 3) – Running unit tests & code coverage


This is the 3rd in a series of posts about unit testing:

Unit Testing (part 1) - Without using a mocking framework

Unit Testing (part 2) - Faking the HttpContext and HttpContextBase

Unit Testing (part 3) - Running Unit Tests & Code Coverage

Unit Testing (part 4) - Faking Entity Framework code first DbContext & DbSet

 

The new way of running unit tests from within Visual Studio or as part of a TFS build is by making use of the TestSettings.RunSettings file (the old file was TestSettings.TestSettings).

This is the TestSettings.RunSettings file from Microsoft with our exclusions added to it in yellow.

What this does is when the code coverage tool is run it excludes certain code from that process such as the code first migrations files or 3rd party DLL’s (as we cannot unit test them) so we need to exclude them otherwise our code coverage stats will never reach or get close to 100%!.

 <?xml version="1.0" encoding="utf-8"?>

<!-- File name extension must be .runsettings -->

<RunSettings>

  <DataCollectionRunSettings>

    <DataCollectors>

      <DataCollector friendlyName="Code Coverage" uri="datacollector://Microsoft/CodeCoverage/2.0" assemblyQualifiedName="Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector, Microsoft.VisualStudio.TraceCollector, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">

        <Configuration>

          <CodeCoverage>

<!--

Additional paths to search for .pdb (symbol) files. Symbols must be found for modules to be instrumented.

If .pdb files are in the same folder as the .dll or .exe files, they are automatically found. Otherwise, specify them here.

Note that searching for symbols increases code coverage runtime. So keep this small and local.

-->

<!--          

            <SymbolSearchPaths>             

                   <Path>C:\Users\User\Documents\Visual Studio 2012\Projects\ProjectX\bin\Debug</Path>

                   <Path>\\mybuildshare\builds\ProjectX</Path>

            </SymbolSearchPaths>

-->

<!--

About include/exclude lists:

Empty "Include" clauses imply all; empty "Exclude" clauses imply none.

Each element in the list is a regular expression (ECMAScript syntax). See http://msdn.microsoft.com/library/2k3te2cs.aspx.

An item must first match at least one entry in the include list to be included.

Included items must then not match any entries in the exclude list to remain included.

-->

            <!-- Match assembly file paths: -->

            <ModulePaths>

              <Include>

                <ModulePath>.*\.dll$</ModulePath>

                <ModulePath>.*\.exe$</ModulePath>

              </Include>

              <Exclude>

                <ModulePath>.*CPPUnitTestFramework.*</ModulePath>

                <ModulePath>.*stackexchange.*</ModulePath>

                <ModulePath>.*antlr3.*</ModulePath>

                <ModulePath>.*microsoft.practices.*</ModulePath>

              </Exclude>

            </ModulePaths>

 

            <!-- Match fully qualified names of functions: -->

            <!-- (Use "\." to delimit namespaces in C# or Visual Basic, "::" in C++.)  -->

            <Functions>

              <Exclude>

                <Function>^Fabrikam\.UnitTest\..*</Function>        

                <Function>^std::.*</Function>

                <Function>^ATL::.*</Function>

                <Function>.*::__GetTestMethodInfo.*</Function>

                <Function>^Microsoft::VisualStudio::CppCodeCoverageFramework::.*</Function>

                <Function>^Microsoft::VisualStudio::CppUnitTestFramework::.*</Function>

                <Function>^SiteDataAccess\.Core\.Migrations\..*</Function>

              </Exclude>

            </Functions>

 

            <!-- Match attributes on any code element: -->

            <Attributes>

              <Exclude>

                <!--Don't forget "Attribute" at the end of the name -->

                <Attribute>^System.Diagnostics.DebuggerHiddenAttribute$</Attribute>

                <Attribute>^System.Diagnostics.DebuggerNonUserCodeAttribute$</Attribute>

                <Attribute>^System.Runtime.CompilerServices.CompilerGeneratedAttribute$</Attribute>

                <Attribute>^System.CodeDom.Compiler.GeneratedCodeAttribute$</Attribute>

                <Attribute>^System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute$</Attribute>

              </Exclude>

            </Attributes>

 

            <!-- Match the path of the source files in which each method is defined: -->

            <Sources>

              <Exclude>

                <Source>.*\\atlmfc\\.*</Source>

                <Source>.*\\vctools\\.*</Source>

                <Source>.*\\public\\sdk\\.*</Source>

                <Source>.*\\microsoft sdks\\.*</Source>

                <Source>.*\\vc\\include\\.*</Source>

              </Exclude>

            </Sources>

 

            <!-- Match the company name property in the assembly: -->

            <CompanyNames>

              <Exclude>

                <CompanyName>.*microsoft.*</CompanyName>

              </Exclude>

            </CompanyNames>

 

            <!-- Match the public key token of a signed assembly: -->

            <PublicKeyTokens>

              <!-- Exclude Visual Studio extensions: -->

              <Exclude>

                <PublicKeyToken>^B77A5C561934E089$</PublicKeyToken>

                <PublicKeyToken>^B03F5F7F11D50A3A$</PublicKeyToken>

                <PublicKeyToken>^31BF3856AD364E35$</PublicKeyToken>

                <PublicKeyToken>^89845DCD8080CC91$</PublicKeyToken>

                <PublicKeyToken>^71E9BCE111E9429C$</PublicKeyToken>

                <PublicKeyToken>^8F50407C4E9E73B6$</PublicKeyToken>

                <PublicKeyToken>^E361AF139669C375$</PublicKeyToken>

              </Exclude>

            </PublicKeyTokens>

 

            <!-- We recommend you do not change the following values: -->

            <UseVerifiableInstrumentation>True</UseVerifiableInstrumentation>

            <AllowLowIntegrityProcesses>True</AllowLowIntegrityProcesses>

            <CollectFromChildProcesses>True</CollectFromChildProcesses>

            <CollectAspDotNet>False</CollectAspDotNet>

          </CodeCoverage>

        </Configuration>

      </DataCollector>

    </DataCollectors>

  </DataCollectionRunSettings>

</RunSettings>

 

Save this file and then in Visual Studio go to Test -> Test Settings and select this .RunSettings file.  And then run the tests.

If you have Visual Studio Enterprise you can automatically run the tests on every build by clicking this icon

image

We should be running the unit tests frequently and before we merge our code to the main branch.

To use Code Coverage (again an Enterprise thing) Right click in test explorer and choose ‘Analyze Code Coverage for Selected Tests’.

image

All the tests will then be run and the ‘Code Coverage Results’ window will appear.

image

Clicking the icon circled above will show all your code highlighted so at a glance as you navigate your code you can see what is covered by the unit tests.

·         Blue = Unit tests are testing it

·         Red = No tests are testing this code yet!

image

We should be running code Analysis before we merge to main to ensure our code is being tested.

 

Using the same .RunSettings file on a TFS build shows this:

image

And in the TFS Build Summary report:

image

(Note: I only have code coverage and unit tests’ running on main branch merges.)