Blog

Using Octopus Deploy and Gulp with TFSBuild 2013
 

If you’d like to use these technologies together, it won’t be hard, however, to save you some time & for myself in the future, let me show you what I did. For some reason, I have tendency to go through all kind of different problems.

For my example, let’s assume that I have a SPA project called “HelloWorld“, which is using angularjs, gulp, bower, and I want to deploy it to to production. To keep things simple, we are using gulp only to produce the “final minimized html/js files” for production environment, eg single environment.

You could also create an hybrid solution that uses octopus transformations for some of the Javascript / html files.

Once you understand how to do this whole process for a single environment, it will be easy to extend this further. You could make gulp produce three folders (dev, Qa, Production), and these will be picked up by OctoPack, later, in OctopusDeploy, you can create step template to pick up the correct folder, depending on the target environment.

Ncapsulate is a NuGet wrapper for NodeJS and libraries of the Node Ecosystem. It is very simple, you just install it as a nuget package onto your project, modify the target files, and voila, you now have ability to use gulp using nodejs. You don’t need to install nodejs. It’s already included within NCapsulate, and can be used as it is.

The whole process should take you no more than 5 minutes, minimal configuration, no need to install anything, besides nuget packages.

Do this before running install-package:
1) Close Visual Studio
2) Open PowerShell as admin, and run following command:

Set-ExecutionPolicy RemoteSigned

3) Open Visual Studio again, and run the install-package again. You might want to try removing everything manually that was partially done.

Let’s start by installing nugets to your project:

install-package OctoPack
install-package NCapsulate.Node
install-package NCapsulate.Gulp

Do it in that order! If I recall right, NCapsulate.Gulp depends on NCapsulate.Node, however, if it’s not present, it will pull in old version of NCapsulate.Node, which can cause problems in the future.

Make sure you have following things checked in TFS:
1) Empty node_modules directory (located in the same place as Gulp.cmd
2) Gulp.cmd
3) Node.cmd
4) Npm.cmd

Our gulpfile.js is really simple. Just creating a task that builds necessary files for production environment into ‘dist’ folder:

...

'use strict';

var gulp = require('gulp');

 

var options = {

   src: 'src',

   dist: 'dist',

   tmp: '.tmp'

},

wiredep: {

   directory: 'bower_components',

   exclude: [/jquery/, /bootstrap\.js/, /bootstrap\.css/]

}

};

 

gulp.task('build-dist', ['clean'], function () {

   gulp.start('build');

});

...

Let’s edit App_Build/gulp.targets, and call our gulp task.

<Target Name="Gulp-AfterBuild" AfterTargets="AfterCompile">

   <Gulp Tasks="build-dist" />

</Target>

As you compile the project on a computer that has latest sources from TFS, it’s supposed to restore the nuget packages, and after that, install the eco system of nodejs, gulp, npm. If that is done, it’s trying to run your gulp task, which will build final output into dist folder.

As that’s done, OctoPack will run and pick up the dist folder, which then can be deployed to anywhere.

That’s it!

.. not so fast, there are few hurdles.

Hurdle #1 (NCapsulate failing)

Inconsistently, the Task could not be loaded from the Assembly.
I have no idea what that was about, but I faced the same issue. For me, restarting visual studio & upgrading to the latest version of NCapsulate seemed to do the trick. I have not seen this error after many builds.

Hurdle #2 (TFS files read-only)

Files are read-only in TFS build server. As the build server does the “Get latest”, all files on hard disk are read-only. This is bad as the nodejs ecosystem might need to change the file called package.json. Simple solution is to get rid of the read-only flag before build.

<!---TFS build server marks all the files as read/only,
however, npm install needs to modify package.json.
The simplest solution is to disable the read/only flag on a file. -->
<Target Name="BeforeBuild">
   <Exec Command="attrib -R &quot;$(ProjectDir)\package.json&quot;" />
</Target>

Hurdle #3 (PhantomJS being a little bit buggy)

Failed at the phantomjs@1.9.17 install script ‘node install.js’
For some reason, I kept getting that error, and it seems to be known issue. I did two things to fix this issue, however, I don’t think the later is needed anymore.

1) Upgraded NCapsulate. If you do that, it will make sure we are using latest version of NodeJS.
2) Modified my npm to use ‘call’ prefix, and disable SSL:

@echo on
call "..\packages\Ncapsulate.Node.0.10.35.1\nodejs\npm" set strict-ssl false
call "..\packages\Ncapsulate.Node.0.10.35.1\nodejs\npm" %*
@echo on

We need to have ‘call’ in front of every npm call, as bat file is too stupid to return back otherwise.

Hurdle #4 (OctoPack being picky)

Are we done? Almost! To my surprise, OctoPack didn’t pick up “dist” folder. It makes sense. I did some reverse-engineering & wrote some code for MSBuild.

In a nutshell, you have to get rid of everything that is not related to “dist” folder, and include the dist folder as apart of MSBuild process,
which then will be found by OctoPack.

It’s also important to do these things in correct order, as they both hook themselves into “AfterBuild” step.

<!-- Move dist folder into specific item group, as that will be picked up by OctoPack. -->
<Target Name="AfterBuild">
  <ItemGroup>
    <FileWrites Remove="@(FileWrites)" />
    <FileWritesShareable Remove="@(FileWritesShareable)" />
    <Content Remove="@(Content)" />
    <TypeScriptCompile Remove="@(TypeScriptCompile)" />
    <StyleCopOutputFile Remove="@(StyleCopOutputFile)" />
    <StyleCopFiles Remove="@(StyleCopFiles)" />
  </ItemGroup>
  <CreateItem Include="$(ProjectDir)\dist\**">
    <Output ItemName="FileWrites" TaskParameter="Include" />
  </CreateItem>
</Target>

<!--OctoPack shoud be imported as last step -->
<Import Project="..\packages\OctoPack.3.0.42\tools\OctoPack.targets" Condition="Exists('..\packages\OctoPack.3.0.42\tools\OctoPack.targets')" />

In fact, it’s not necessary to do it like so. You can create new nuspec file, and add the folder into ‘files’ element in nuspec file, to include dist folder.

Hurdle #5 (Running nodejs as part of TFSBuild)

To be honest, windows and nodejs don’t play nicely. It’s because node likes to have extra-long paths, which causes all kind of different problems. Most often, you will get error about file path being too long, if TFS tries to clean the workspace.

I solved it easily, by writing powershell script that wipes out every bin/obj folder, and set the “Clean workspace” to false, in TFS build definiton. It doesn’t try to delete any folders.

Happy gulping!

Also, you might want to host mirrored repo of npm in your company, as that will speed up restoring of things.

1 comment
  • corey

    Great artical. I’ve been struggling with getting all this to work myself. Everything is working as expected…BUT

    in my TFS Build log I keep getting this.

    9>Gulp-BeforeBuild:
    [22:13:04] Local gulp not found in D:\Builds\35_111\src\Web\CaseManagement.Web

    I thought the package.json would be read and install any dependency npm’s that are missing?

    And I’ve also tried AfterBuild and get the same result.

Reply

Your email addres will not be published. All fields are required.