📜 ⬆️ ⬇️

We use assembly scripts on F # / C # in TFS 2012

I think I’m not the only one who remembers Microsoft with bad words when I have to modify and / or expand the so-called build process template in TFS. Under the cut, the story of how we moved from Xaml to F # / C # scripts. As we tried to integrate Fake into TFS, but in the end we got our own solution - AnFake .

The article will be useful to those who use TFS as a CI solution, but not enthusiastic about its build process templates.

During the development of our project (which, in essence, is a binary-supplied module for, in fact, the company's product), we realized that I want to have a CI-process a little more complicated than just compiling and running unit tests.

This is what the “perfect” process looked like in our eyes.
')
Regular build:
Supply:

The workflow in our company is built entirely on TFS, including tracking of bugs and tasks; version control and continuous integration. CI assemblies in TFS are specified using the build process template. The first thing we tried to do to realize our “ideal” was to set up a process template for ourselves and ... faced a number of difficulties.

1. Part of the assembly logic changes as the module develops (in particular, preparation of test samples), i.e. It is desirable to keep this logic in the version control system together with other sources of the module. But the process template in TFS 2012/2013 is stored separately and is not versioned (yes, it lies in the VCS, but only the latest version is always used). Why is this a problem? We have at least two branches: developmental and stable (theoretically, release branches can still arise if hotfixes need to be done) and, at least, some assembly steps in them are different, i.e. we cannot collect all branches using the same template.

Possible solutions:

We chose the script. At the first stage - the usual bat-file.

2. Using the script led us to another problem - the information content of logging has fallen sharply. Messages are simply written to the console, they have no levels, it is impossible to distinguish a warning from a diagnostic. You can only highlight the error by writing a message to stderr. But each line read by TFS from stderr will be taken as a separate error. Thus, for example, a formatted 3-line error message in TFS will be lit as 3 separate errors. “Not neatly”, but you can live.

3. The script began to grow. In addition to the sample preparation step, the performance tests and FP / TP tests soon appeared in it. Why is that? It would seem that the launch of tests should be in the template. The fact is that these tests are carried out by tools that are also part of our module and, accordingly, are developed together with it, therefore the launch method also changes. Leaving them in the template, we have the problem already considered in item 1.

Further more. The desire to have access from the script to the properties of the current assembly (the name of the branch, the version number, etc.) and to communicate with other assemblies for the organization of the conveyor began to mature.

At this stage, it became clear that the script should be more powerful than just a bat file. And considering that it’s not so hot to mess around with the process templates, there was a desire to transfer all the logic to the script, and to use the template only as a wrapper for siphoning source codes and transferring control to the script.

PSake , Rake and Fake have become candidates for the script. After a cursory analysis, PSake was dropped because of the PowerShell syntax; Rake - because no one in our team was familiar with Ruby; and stayed fake. For justice, I must say that at that time F # was not known to the team either, however F # was supported by Visual Studio out of the box, so we stopped at it.

Unfortunately, Fake is not friendly with TFS. But what, we are not programmers or what ?! Screw themselves. It can be said that problems 1 and 3 have been resolved. However, problem 2 has worsened, because now the whole build went in a script, the result shown in Visual Studio looked depressing - there are no warnings visible (including from the compiler); what stages were performed - not visible; errors are shown by creepy "footwomen". We tried to solve this problem by writing an extension for Fake, but Fake did not have a single logging point from which it would be possible to redirect the structured output to TFS. Along the way, a few more dissatisfactions arose (you can read more here ).

However, the idea to use a full-fledged programming language to describe the assembly is like. In the end, I decided at my leisure to embody the idea of ​​Fake in my own performance. The emphasis was on:

Besides, remembering my first attempts to understand F # scripts, I thought: “it would be nice to be able to write scripts in C # too” and included this requirement in the work script. Yes, you can write an assembly script in C #, however, unfortunately, IntelliSense does not work in this case - the studio does not know that C # may be a script.

The result was a very decent tool (I called it AnFake = Another F # Make), which can be useful to anyone who “fights” with TFS. Let's see how it looks and what it can do (at the moment AnFake is mainly designed for TFS, so the following presentation will go in the context of TFS).

Suppose we have a solution called Demo, which lies in the TFS version control system:

$/TeamProject/Demo /dev /Demo.App /Demo.Lib /Demo.Lib.Test Demo.sln 

Let us also have a workspace of 'Demo.dev' configured with a single mapping:

 $/TeamProject/Demo/dev: C:\Projects\Demo.dev 

(hereinafter, it is assumed everywhere that the scheme “one workspace per branch” is used)

Open C: \ Projects \ Demo.dev \ Demo.sln in Visual Studio. Install AnFake as a NuGet package:

 PM> Install-Package AnFake 

When installing the package in the root folder of the solution, several files will be created:
(by the way, the installation script is also an F # script for AnFake)

The base build.fsx script looks like this:

 Tfs.PlugIn() let out = ~~".out" let productOut = out / "product" let testsOut = out / "tests" let tests = !!"*/*.Test.csproj" let product = !!"*/*.csproj" - tests "Clean" => (fun _ -> let obj = !!!"*/obj" let bin = !!!"*/bin" Folders.Clean obj Folders.Clean bin Folders.Clean out ) "Compile" => (fun _ -> MsBuild.BuildRelease(product, productOut) MsBuild.BuildRelease(tests, testsOut) ) "Test.Unit" => (fun _ -> VsTest.Run(testsOut % "*.Test.dll") ) "Test" <== ["Test.Unit"] "Build" <== ["Compile"; "Test"] 

... the same on C #
 Tfs.PlugIn(); var outDir = ".out".AsPath(); var productOut = out / "product"; var testsOut = out / "tests"; var tests = "*/*.Test.csproj".AsFileSet(); var product = "*/*.csproj".AsFileSet() - tests; "Clean".AsTarget().Do(() => { var obj = "*/obj".AsFolderSet(); var bin = "*/bin".AsFolderSet(); Folders.Clean(obj); Folders.Clean(bin); Folders.Clean(out); }); "Compile".AsTarget().Do(() => { MsBuild.BuildRelease(product, productOut); MsBuild.BuildRelease(tests, testsOut); }); "Test.Unit".AsTarget().Do(() => { VsTest.Run(testsOut % "*.Test.dll"); }); "Test".AsTarget().DependsOn("Test.Unit"); "Build".AsTarget().DependsOn("Compile", "Test"); 


In principle, this is already enough to run the local assembly:

 PM> .\anf Build 

(here we used the Package Manager Console, but AnFake can be run from any command line console)

As a result, we get something like this report:



We see that the compilation passed with one warning; 2 tests were performed, one of which is Skipped. Ok, we commit changes. The commit will contain a .nuget / packages.config file (here NuGet writes solution-level packages) and files created during the installation of AnFake in the root folder of the solution.

Now run the same build via TFS. To do this, you need to install a special template AnFakeTemplate.xaml (done only once for a team project):

 PM> .\anf "[AnFakeExtras]/vs-setup.fsx" "BuiltTemplate" -p "TeamProject" 

where instead of TeamProject, of course, you need to substitute the name of your project in TFS.

The command will create a temporary workspace named AnFake.BuildTemplate.yyyymmdd.hhmmss; will download to the temporary folder $ / TeamProject / BuildProcessTemplates; will add the AnFakeTemplate.xaml template and several related libraries. The team does NOT commit anything automatically so as not to cause a storm of fair indignation. Therefore, we go to Visual Studio -> Team Explorer -> Pending Changes, switch to workspace AnFake.BuildTemplate, review the changes (make sure that there is nothing superfluous) and commit.

Now we can create a build definition:
We start the assembly according to the definition we just created: Queue Build in the context menu. As a result, we get the following report:


The AnFakeTemplate template takes three simple steps:

The template is quite simple and maintains compatibility for a number of versions. This way you can upgrade the AnFake package without having to update the template. It is even possible to have different versions of AnFake in different solutions, and they will be safely assembled with one pattern.

I demonstrated the basic AnFake TFS integration scenario. However, integration is not limited to a template: it is possible to access the properties of the current assembly from a script; access artifacts from other builds; there is even the possibility to organize a conveyor. In addition, AnFake provides additional automation when working with mappings and workspaces: mappings can be stored in VCS along with the rest of the project sources, the workspace will be created and updated automatically.

I do not want to overload the article with a description of all these buns here and now, but if this approach is interesting and the tool is in demand, I will write a sequel soon. In conclusion, I would like to say that the “ideal” build, described at the beginning of the article, is currently fully implemented and works on AnFake. I can not show our real scripts, but some interesting excerpts can be viewed here .

I would be grateful for the feedback. Thank.

Source: https://habr.com/ru/post/256543/


All Articles