Azure Web Apps Source Deployments with Gulp

In my last blog post “Ionic Framework as a Web Site” I detailed some changes I need to make to an Ionic app to utilize it as a web site. Step 2 of that process was trying to land my new Ionic Site somewhere. As Resgrid is a 100% cloud company we put it on Azure. So we created an Azure Web App, pointed it at our source control and then it all broke. The structure of source we use for Ionic or AppBuilder doesn’t lend itself to becoming a website from the folder structure, as is the common use case for source control provider deployments. The solution, Gulp!

azure-app-service-with-office-365-and-yammer-sharepoint-saturday-florence-2015-session-8-638

Azure uses an open source project called Kudu as an engine for executing git/hg deployments, running tasks and even WebJobs. This allows your to take code from a source code repository have Azure pick it up and deliver it to the cloud and run additional operations.

Note that Kudu itself isn’t limited to Gulp, you can run pretty much anything the underlying Azure VM has access to. Creating the script is pretty simple. If you have the Azure CLI tools installed you can run the following command:

azure site deploymentscript

Note there are a number of default switches you can use for example –aspWebSite for your ASP/MVC/WebAPI sites, –node, –php, –python and finally –basic. This command will create 2 files, a .deployment file and a deploy.cmd file. For the Ionic Framework one we used –node as our switch. The .deployment file is pretty simple:

[config]

command = deploy.cmd

This just a configuration file for Azure to tell it what to run. There is a lot you can do with with the .deployment file. The other file created is the deploy.cmd file, below is the one used in Resgrid’s Ionic Framework app deployment. I cut the code above out for brevity, at the end of the Setup section, just above this section of code is the “IF NOT DEFINED KUDU_SYNC_CMD” check.

:: Code above omitted for brevity

IF NOT DEFINED GULP_CMD (
  :: Install gulp
  echo Installing Gulp
  call npm --registry "http://registry.npmjs.org/" install gulp -g --silent
  IF !ERRORLEVEL! NEQ 0 goto error

  :: Locally just running "gulp" would also work
  SET GULP_CMD="%appdata%\npm\gulp.cmd"

)

goto Deployment

:: Utility Functions
:: -----------------

:SelectNodeVersion

IF DEFINED KUDU_SELECT_NODE_VERSION_CMD (
  :: The following are done only on Windows Azure Websites environment
  call %KUDU_SELECT_NODE_VERSION_CMD% "%DEPLOYMENT_SOURCE%" "%DEPLOYMENT_TARGET%" "%DEPLOYMENT_TEMP%"
  IF !ERRORLEVEL! NEQ 0 goto error

  IF EXIST "%DEPLOYMENT_TEMP%\__nodeVersion.tmp" (
    SET /p NODE_EXE=<"%DEPLOYMENT_TEMP%\__nodeVersion.tmp"
    IF !ERRORLEVEL! NEQ 0 goto error
  )
  
  IF EXIST "%DEPLOYMENT_TEMP%\__npmVersion.tmp" (
    SET /p NPM_JS_PATH=<"%DEPLOYMENT_TEMP%\__npmVersion.tmp"
    IF !ERRORLEVEL! NEQ 0 goto error
  )

  IF NOT DEFINED NODE_EXE (
    SET NODE_EXE=node
  )

  SET NPM_CMD="!NODE_EXE!" "!NPM_JS_PATH!"
) ELSE (
  SET NPM_CMD=npm
  SET NODE_EXE=node
)

goto :EOF

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Deployment
:: ----------

:Deployment
echo Handling custom app deployment

::  Select node version
call :SelectNodeVersion

::  Install NPM Packages
IF EXIST "%DEPLOYMENT_SOURCE%\package.json" (
  pushd "%DEPLOYMENT_SOURCE%"
  echo Installing NPM Dependicies.
  call :ExecuteCmd !NPM_CMD! install
  IF !ERRORLEVEL! NEQ 0 goto error
  popd
)

:: Run Gulp to build website from mobile app
IF EXIST "%DEPLOYMENT_SOURCE%\gulpfile.js" (
  pushd "%DEPLOYMENT_SOURCE%"
  echo Running Gulp.
  call :ExecuteCmd !GULP_CMD! web:build
  IF !ERRORLEVEL! NEQ 0 goto error
  popd
)

:: KuduSync
echo Handling Basic Web Site deployment.
IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" (
  call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_SOURCE%\web" -t "%DEPLOYMENT_TARGET%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd"
  IF !ERRORLEVEL! NEQ 0 goto error
)

The Utility functions are important. They setup the environment variables for Node and NPM. Note that Azure typically runs a few versions older then the current node version. Additionally some NPM dependencies, like fibers, that require locally complication (and thus Visual Studio or some other compiler) installed won’t work on Azure.

The Deployment section is where the big stuff happens. We first call our SelectNodeVersion utility function to setup Node and NPM then run NPM to install our modules. Again be cautious of what node modules you add, keep it to the bare minimum.

The next step is we run our gulp command against our gulpfile.js and call our task to be run, in this case it’s “web:build”. There is no Azure specific items in our gulpfile, we just copy our web items into a folder “called web at the root of our solution” and for KuduSync (the command that handles copying to the wwwroot) we point it to “%DEPLOYMENT_SOURCE%\web”.

Azure is a great PaaS (Platform as a Service) for your Microsoft and non-Microsoft technologies. Personally I feel that their PaaS capabilities far exceed any other cloud provider out there and when solving problems like the one above, using zero MS Tech, I was easily able to accomplish my goals without ever touching a VM or worrying about configuration.

Resgrid is a SaaS product utilizing Microsoft Azure, providing logistics, management and communication tools to first responder organizations like volunteer fire departments, career fire departments, EMS, search and rescue, CERT, public safety, disaster relief organizations, etc. It was founded in late 2012 by myself and Jason Jarrett (staxmanade).

About: Shawn Jackson

I’ve spent the last 18 years in the world of Information Technology on both the IT and Development sides of the aisle. I’m currently a Software Engineer for Paylocity. In addition to working at Paylocity, I’m also the Founder of Resgrid, a cloud services company dedicated to providing logistics and management solutions to first responder organizations, volunteer and career fire departments, EMS, ambulance services, search and rescue, public safety, HAZMAT and others.