Skip to content

Instantly share code, notes, and snippets.

@thomassvensen
Last active March 29, 2016 13:44
Show Gist options
  • Save thomassvensen/0319093c260650e7d8c7 to your computer and use it in GitHub Desktop.
Save thomassvensen/0319093c260650e7d8c7 to your computer and use it in GitHub Desktop.
We are using Jenkins on Windows as our build server. Jenkins is not state of the art in looks or simplicity,
but it sure competes with anything in terms of configurability and real-world experiences. There is aslo no
arguing that Jenkins works best on *NIX, but Windows support is pretty good.
We started out with a normal [MSBuild](http://wiki.jenkins-ci.org/display/JENKINS/MSBuild+Plugin) action, the basic steps were:
1. Get latest from GitHub
1. Restore Nuget packages
1. Run MSBuild
1. Push to Octopus Deploy
Execution time for the whole process was around 1 minute 20 seconds, which we found acceptable.
Then we added Gulp to build our frontend CSS and JavaScript code. We installed Node.JS and added the
following stage to our build pipeline:
![image](https://cloud.githubusercontent.com/assets/77299/14107522/0c656290-f5b9-11e5-8182-f88cc1c32279.png)
It ran fine, but execution time was up from 1:20 to around 4 minutes. The reason was obvious: the
initial ```npm install``` took forever to complete. Closer investigation showed that it downloaded over 10.000
files! Node.js and npm seems to have taken "divide and conquer" to an absurd level...
Anyway, we had to find a way to deal with this madness. Quick research suggested that the ```--global``` flag
could be an easy solution. We tried it, but strange things happened. The files were downloaded into
the obscure path ```C:\Windows\System32\Config\systemprofile```, where they mysteriously disappeared immediately
after download. The files are placed there because Jenkins runs under the default "Local system" profile,
which we already knew caused various problems, e.g., with SSH private keys. A possible solution would be to
create a new system account under which we ran Jenkins, but as we were not administrators of Jenkins,
and it could have other side effects that we did not know, it was not a tempting approach.
What we wanted was to have a local copy of the 10.000 npm files somewhere within the Jenkins file structure
that didn’t get wiped out after every build. Jenkins creates a ```workspace``` folder under each project, like this:
```
c:\jenkins\projects\our-project\workspace
```
Now, if we could put the npm files inside ```our-project``` and reference it there, we would avoid the download
penalty. Fortunately, Windows has implemented the soft links feature known from UNIX. This allows us to fool
npm into thinking it works on a local folder when it is actually located elsewhere. Specifically, we chose to store
the npm files in this file path
```
c:\jenkins\projects\our-project\filecache\node_modules
```
where "filecache" is just a name chosen to (hopefully) explain its purpose for any newcomers to the project.
We then updated our build pipeline to do the following (a little simplified):
1. ```if not exist "..\filecache\node_modules" mkdir ..\filecache\node_modules```
1. ```mklink /j node_modules ..\filecache\node_modules```
1. ```npm install```
1. ```gulp.js css```
1. ```rmdir node_modules ..\filecache\node_modules``` (removes link to prevent wiping)
With this, the ```npm install``` step only takes a few seconds, just verifying that all files are up to date.
Once we had the above setup in place, we saw that the same logic applied to our ```nuget restore``` stage.
Although faster than ```npm install```, Nuget also downloaded the same bunch of files for every build.
The solution above could be copy-pasted, replacing "node_modules" with "packages", and the
build time was decreased by another 20 seconds. The end result was build times back at around the initial
1 minute and 20 seconds, except now doing a full frontend build as well!
Here is the end result, with some gory details left out in the previous discussion:
![image](https://cloud.githubusercontent.com/assets/77299/14109082/b25c397e-f5c0-11e5-8fa7-66513e972002.png)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment