How I run a personal CI build every time I build my code in Visual Studio.
Summary
This week we’ll finish the series by creating the Visual Studio macro that automatically runs the Personal CI process every time we build the code.
The Series
In this series I show you how to take continuous integration to the extreme by building a personal CI server that runs all your tests and adds a commit to source control every time you build in Visual Studio.
- Extreme CI Part 1 – Overview
- Extreme CI Part 2 – Setting Up Git
- Extreme CI Part 3 – The Visual Studio Macro (you are here)
Setting Up the External Command in Visual Studio
Last week we created a batch file to commit and push our changes to the CI server. Now we need to execute that batch file from within Visual Studio. We’ll do it using an external command. To set up the external command in Visual Studio:
- Select Tools –> External Tools… from within Visual Studio. I am using Visual Studio 2008, for 2010, it may be a bit different.
- When the “External Tools†dialog shows, click “Add†and fill in the following:
- Title: Commit and Push to Personal CI
- Command: $(SolutionDir)\..\CommitAndPushToPersonalCI.bat
- Initial directory: $(SolutionDir)\..\
- Use Output window: checked
When you are done, the dialog should look like this:
Let me clarify a few things.
Why did we use $(SolutionDir)\..\?
The $(SolutionDir) is the directory containing the current solution, so when I am specifying $(SolutionDir)\..\, I am specifying the parent directory of the solution’s directory. This is because I have the following folder structure.
+-MyProject <-- Root of Git Repository +-src <-- Solution Location +-lib
I want to commit and push from the root of my repository so that anything I have added to the lib or any other folders will be commited. However, my solution is in the src folder, which is one level deeper than I want to commit from. You may need to adjust your settings based on your project structure.
What’s up with the Use Output window setting?
Checking the “Use Output window†option redirects the console output of the batch file to the output window in Visual Studio.
Ok, let’s move on.
Now your tools menu should contain the new command and look like this.
And when running your custom command, its results appear in the output window.
Now lets take the final step and create the Visual Studio macro that runs this every time we build.
Create the Visual Studio Macro
To create the macro, we need to open up the Macros IDE.
Select Tools –> Macros –> Macros IDE… from within Visual Studio, or press Alt+F11. The Macros IDE will launch.
Open the EnvironmentEvents module under the MyMacros project. It should look like the screenshot above. Take the following code and insert it after the automatically generated code, and before the End Module.
Dim BuildWasSuccessful As Boolean Dim BuildingSolution As Boolean Dim RestrictToSolutionName As String Dim PersonalCICommandName As String Private Sub BuildEvents_OnBuildBegin(ByVal Scope As EnvDTE.vsBuildScope, ByVal Action As EnvDTE.vsBuildAction) Handles BuildEvents.OnBuildBegin 'Initialize variables when the build starts. BuildWasSuccessful = True BuildingSolution = True 'Only commit to personal CI for the specified solution name. RestrictToSolutionName = "MyProject" 'Name of the external Personal CI command. PersonalCICommandName = "Tools.ExternalCommand3" End Sub Private Sub BuildEvents_OnBuildDone(ByVal Scope As EnvDTE.vsBuildScope, ByVal Action As EnvDTE.vsBuildAction) Handles BuildEvents.OnBuildDone 'Don't run it if we just did a clean, since its not a build. If Action = vsBuildAction.vsBuildActionClean Then BuildingSolution = False Exit Sub End If 'Only run autocommit if the build succeeded. If BuildWasSuccessful And DTE.Solution.FullName.Contains(RestrictToSolutionName) Then 'Run the command to autocommit to git. DTE.ExecuteCommand(PersonalCICommandName) End If BuildingSolution = False End Sub Private Sub BuildEvents_OnBuildProjConfigDone(ByVal Project As String, ByVal ProjectConfig As String, ByVal Platform As String, ByVal SolutionConfig As String, ByVal Success As Boolean) Handles BuildEvents.OnBuildProjConfigDone If Success = False Then 'Set the build to failed. BuildWasSuccessful = False 'The build failed...cancel any further builds. DTE.ExecuteCommand("Build.Cancel") End If 'Only commit project level builds if we are not building the whole solution If Success = True And BuildingSolution = False And DTE.Solution.FullName.Contains(RestrictToSolutionName) Then 'Run the command to autocommit to git. DTE.ExecuteCommand(PersonalCICommandName) End If End Sub
To get it to work with your project, you need to change two variables: RestrictToSolutionName and PersonalCICommandName.
RestrictToSolutionName
This value is used to restrict the personal CI process to one solution. If it executes on a solution that is not set up properly, it will fail. If you want to use this script for multiple solutions, you will need to change it. If you do, send me a copy and I’ll update this post and give you the credit.
PersonalCICommandName
This is the name of the ExternalCommand you created in visual studio. It is tricky to get the right name because it depends upon the number of external commands you have. It does not use the name you gave it. Pity.
The command names are in the form “Tools.ExternalCommandN†where N is a number. One way to determine the name is to look at your Tools menu and see what position that command is in. In the case of our example, it is the third custom tool in the tools menu, so it is “Tools.ExternalCommand3â€. If that doesn’t work, you can create a custom toolbar and add the external commands to it one at a time. When you view the toolbar, you will see the real name of the command and can determine which is yours.
One More Thing
Once you set those properties and save the script, try building your project. It should automatically commit the code and push it up to the CI repository.
Now all you have to do it hook up a CI Server to it. I leave that as an exercise to you. It is beyond the scope of this article. But, you’re a good developer. You’ve setup a CI server before, haven’t you?
Challenge
I pray this series has helped you tighten up your feedback loop so that when tests fail, you know…fast. My challenge to you is, how can we tighten it up even further? I am open to suggestions.
-Chris
2 Trackbacks
[…] Extreme CI Part 3 – The Visual Studio Macro […]
[…] Extreme CI Part 3 – The Visual Studio Macro […]