Easy Automated Builds - Part 3
Filed under: #daxmusings #bizapps
In Part 1, we discussed installing the custom activity DLLs into TFS so the build controller and agents can use them. In Part 2, we reviewed where the default build workflows reside in TFS and where we want to inject or Dynamics AX specific activities. So now, we’re ready to actually build a workflow.
Before we begin, there are a few concepts that need explaining. When setting up a workflow, you can specify parameters to make it more generic. This will allow you to share the workflow across multiple build definitions, but run it with slightly different options. These are called “Arguments”. Inside the workflow, you may need to store data in variables to pass from one activity to another in the workflow. For this purpose there are also “Variables”.
In Part 2 we discussed the location to put the activities in. If you deleted the existing items as shown, you’ll need to add a new “Sequence” activity. There are some basic “control flow” activities available in the TFS workflow: Sequence (a list of sequential steps), Parallel (a list of parallel tasks) and some others like foreach loops etc. These are listed in the Toolbox under “Control Flow”. Open the ToolBox from View > Toolbox.
One of the differences again between TFS 2012 and TFS 2013 is that the build in TFS 2012 stores all the build information (build number, source directory, binary output directory, etc.) in variables in the workflow. In TFS 2013 everything gets stored in environment variables. The main idea with TFS 2013 is to be able to attach a PowerShell script pre- and post build (these are arguments to the workflow) so that you can do things without having to modify the workflow. So storing all the information in environment variables solves the issue of having to pass all that info to the PowerShell script. What this means is that we need to add a few extra activities in TFS 2013 (skip this step in TFS 2012) to get some important information. The main things we want to get are the sources directory, the binaries directory, and the build number.
So for a TFS 2013 flow we need to add three new variables. You’ll find the Variables list by click on the “Variables” tab at the bottom of your workflow screen. We will add three Strings and you can leave the scope to “Sequence” which should show up as your default.
Next, find “Team Foundation Build Activities” in the Toolbox, and drag & drop the GetEnvironmentVariable
For the purpose of this workflow, we want three of those, so drag three GetEnvironmentVariable
Microsoft.TeamFoundation.Build.Activities.Extensions.WellKnownEnvironmentVariables.BinariesDirectory Microsoft.TeamFoundation.Build.Activities.Extensions.WellKnownEnvironmentVariables.SourcesDirectory Microsoft.TeamFoundation.Build.Activities.Extensions.WellKnownEnvironmentVariables.BuildNumber
You can give it a nice name for a DisplayName, something like “Get Binaries Directory”. In the “Result” property, we give it the name of the variable we created, to store the string in. If you follow my screenshot, you’ll have a BinariesDirectory, a SourcesDirectory and BuildNumber variable to assign to.
So with that done, we’re back onto our actual work, including TFS 2012 and TFS 2013. Of course there are different ways to conduct the build, and I’ve outlined what we are using in this post but let’s start with something simple here. First, I’m a big fan of “cleaning” an environment. Similar to a build from Visual Studio, you want to remove any artifacts from the last build, especially if you are using one AOS to do multiple builds for multiple code bases. But that brings up a point. You have to evaluate what it is you are trying to do exactly. For this example build I’d like to go “traditional” and assume that we are putting code in, and getting a binary (=ax model) out. So, no need to keep IDs or anything of the sort, we can just blow away all the code, and import all the code anew. To achieve this, you could just uninstall models from all the custom layers. That’s what we initially did but for efficiency we changed to just restoring a base database and modelstore db. This has the advantage that you won’t run into synchronization issues because you uninstalled models. It’s also a bit more predictable in general, and a db restore doesn’t take long at all (of course, or data-database can be empty - we don’t care about data in this environment).
Before we get started, we need to get our custom build activities in the Toolbox. Open the toolbox and right-click somewhere on the window, and select “Choose Items”.
On the “Choose Toolbox Items” dialog, open the “System.Activities Components” tab and click the “Browse” button.
Browse to the location where you saved the DLLs for Visual Studio’s use, as discussed in Part 1. By default, this would be in C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\PublicAssemblies (or 11.0 for VS 2012 and 10.0 for VS 2010). Once opened, you’ll notice the CodeCrib.AX.TFS namespace activities showing up, and they should be checked by default. Click OK to confirm. Now you should see the activities show up under the “General” section.
So, for our workflow we first need to make sure the AOS is stopped. The “StopAOSServer” activity will either stop the AOS if it’s running, or just not do anything if it’s already stopped. Drag & drop the StopAOSServer activity into your workflow. Right-click it and select “Properties”.
Brief intermission. All of the activities in the TFS library are meant for ease of use. That means they contain as many defaults as possible, but as many options as possibly needed in case you want to do advanced things. There are a few key things to understand:
- Anything in these activities will be executed as the build agent user. So, the build agent user needs to be an administrator on the box to have access to start/stop services, it needs access to AX (as a user) for importing XPOs etc, and it needs direct SQL permissions to be able to modify the model store. If you do any sort of file system work like copying files, creating folders, etc - make sure the build agent will understand the paths and have access to them.
- None of the activities REQUIRE any settings to say which AOS or which client configuration to use. By default, it will look for the current user (ie the build agent user) and that user’s default configuration. From that configuration it will also find which AOS we’re working with. However, all activities accept an optional “ConfigurationFile” property, where you can specify a config file. Same thing, it will read the config file to figure out which AOS account it needs to manipulate. If you want the fine details on all the activities’ properties, check the reference page.
So, what I would urge you to do is to create an “Argument” where you can optionally specify a configuration file. Then, make sure all activities point to that argument and your workflow will be most flexible. So for this AOS STOP activity, I created a string argument “DynamicsAXConfigFile”, and then here’s what I have in the AOS properties:
You can optionally specify a time-out on stopping the AOS. Note that that time out is specified in minutes.
So, after the AOS is stopped, there is probably a bunch of things you can do in parallel. First, there’s the matter of combining all XPOs from source control into 1 big XPO file (purpose is to have only one command to import the XPO, and also use AX’s built-in logic to resolve dependencies between objects while importing). You can also call the “Clean” and “DeployReferences” activities, and then some other activity to either restore the database, uninstall the models, or do something of that sorts. Note that these activities may require the use of files (like the combine XPO activity). This is why we have the source directory and binaries directory stored in variables. A few more pointers to explain here:
- The “Sources Directory” is the root folder where the build will retrieve all files. As we will see in our build definition setup in the next part, you can map different source tree folders onto different folders within the sources directory. For our builds, we’ve assumed that we never store anything directly in the root, but a sub-folder for anything needed for the build. For example, the XPO files for the model will be downloaded inside a “\Model” folder. Any third-party DLLs we need for compiling will be stored in “\References”, etc.
- Any files that you put in the “Binaries Folder” will be copied into the “Drop Folder” for the build. Basically, that is the output of your build. We surely want the axmodel exported to go there, but it can be handy to store the full XPO there as well. Or, perhaps you can make a boolean argument to indicate whether or not you want the XPO in the drop folder.
- All property values are VB expressions, and the expected expression output type should equal the type of the property you are trying to set. So for the XPO file for example, you could set the “Folder” property to SourcesDirectory + “\Model”.
- Build number. If you want to provide the Build Number to the CreateModel activity - make sure to change your build number format to a.b.c.d in the build definition. We’ll get to that in the next part.
- For TFS 2012, the binaries and sources folders are already stored in variable names “SourcesDirectory” and “BinariesDirectory”. Other details of the build are stored in a class in variable BuildDetail. You can use BuildDetail.BuildNumber. See the IBuildDetail info on MSDN.
So, here are the properties for my CombineXPOs activity:
Again, for an explanation of all the properties, their defaults and their usage, check the reference page.
In the next part, we’ll dive into creating build definitions and some concepts around that.
There is no comment section here, but I would love to hear your thoughts! Get in touch!