Windows Azure Startup task to run npm install to avoid deploying node modules

Deploying to Windows Azure is a lengthy task (scottgu are you listening?) and you don't want to make it even slower by uploading a package with all the node modules inside. Last week while talking to Johnny we were wondering why Azure is not doing npm install and reading the package.json by default.

It turns out that there is a heated debate apparentely in the node community about whether or not pushing node_modules to the source control is a good idea. My take is: if you wire up your dependencies against fixed versions (instead of latest or greater than) you are safe. So if you think the same, this is a small tutorial of how to configure an Azure role to do the magic. I took many interesting snippets from the great Steve Marx blog.

How to

The process consists basically of:

  1. creating a cmd that downloads npm (not installed in the Azure VM by default) and unzip it to the bin folder where node.exe is.
  2. add the package.json with your dependencies on the role root
  3. configure the startup task in the ServiceDefinition.csdef

The batch that you will run from the startup task looks like this:

install_modules.cmd

cd /d "%~dp0"

if "%EMULATED%"=="true" exit /b 0

echo npm LOG
> npmlog.txt

powershell
-c "set-executionpolicy unrestricted"
powershell
.\download.ps1 "http://npmjs.org/dist/npm-1.1.0-beta-7.zip"

7za x npm-1.1.0-beta-7.zip -y 1>> npmlog.txt 2>> npmlog_error.txt
cd
..
bin
\npm install 1>> npmlog.txt 2>> npmlog_error.txt

echo SUCCESS
exit /b 0

:error

echo FAILED
exit /b -1

There are a couple things worth mentioning

This is the download.ps1.

download.ps1

$url = $args[0];

function download([string]$url) {
    $dest
= $url.substring($url.lastindexof('/')+1)
   
if (!(test-path $dest)) {
       
(new-object system.net.webclient).downloadfile($url, $dest);
   
}
}

download $url

And you can use 7za to unzip or write your own unzipping function with PowerShell (thanks Lito!)

function Extract-Zip
{
    param
([string]$zipfilename, [string] $destination)

   
if(test-path($zipfilename))
   
{            
        $shellApplication
= new-object -com shell.application
        $zipPackage
= $shellApplication.NameSpace($zipfilename)
        $destinationFolder
= $shellApplication.NameSpace($destination)
        $destinationFolder
.CopyHere($zipPackage.Items())
   
}
}

Finally, you will have to add the startup task to the ServiceDefinition.csdef

ServiceDefinition.csdef

<Task commandLine="install_nodemodules.cmd" executionContext="elevated">
   
<Environment>
     
<Variable name="EMULATED">
       
<RoleInstanceValue xpath="/RoleEnvironment/Deployment/@emulated" />
     
</Variable>
   
</Environment>
</Task>

And that's it! enjoy traveling light :)


View the discussion thread.blog comments powered byDisqus