Scott Hanselman

Running the Ruby Middleman Static Site Generator on Microsoft Azure

February 24, '15 Comments [17] Posted in Azure | Open Source | Ruby
Sponsored By

Middleman is "a static site generator using all the shortcuts and tools in modern web development." With any static site generator you can run it all locally and then push/FTP/whatever the resulting HTML to any host. However, static site generators are even more fun when you can host the source code in Git and have your static site build and deploy in the cloud.

Middleman uses Ruby for its build system and views, and some of the Gems it uses are native gems. That means if you are a Windows user, your system will need not just Ruby, but the Ruby DevKit so you can build those native gems. The DevKit is a lovely set of tools that "makes it easy to build and use native C/C++ extensions such as RDiscount and RedCloth for Ruby on Windows."

Azure Websites supports not just ASP.NET today, but also node.js, PHP, Python, and Java, all built in. But not Ruby, directly, yet. Also, Azure Websites doesn't know formally about the idea of a static site generator. You might be thinking, oh, this'll be hard, I'll need to use a VM and do this myself.

However, even though Azure Websites are totally "platform as a service" there's still a Windows Virtual Machine underneath, and you can use the disk space however you like. You've got a LOT of control and can even get a hold of a console where you can run commands and install stuff. The Azure Portal lets you open a command line from your website.

The New Azure Portal

Check me out, here in the new Azure Portal. This is where I did my practice work to see if I could programmatically download and install Ruby via a script. I tried a number of different commands, all from the browser, and explored a number of ideas. When I got it working, I put together a batch file called GetRuby. I could have also used a shell script or PowerShell, but Batch was easy given what I was doing.

ASIDE: You may recognize that console from this video I did about the "Super Secret Debug Console" in Azure. It's not so secret now, it's a feature.  There is still a cool debug "sidecar" website for every Azure site, it's at http://YOURSITENAME.scm.azurewebsites.net/DebugConsole but now a version of the console is in the portal as well.

Azure Websites uses an open source project called Kudu to deploy from locations with source like Github. Kudu supports custom deployment scripts where you can jump in and do whatever you like (within the limits of the security sandbox)

Basically I needed to do these things before running Middleman on my source:

  • Ensure Ruby is installed and in the path.
  • Ensure the DevKit (which includes native compilers, etc) is installed
  • Initialize and setup DevKit for builds
  • Update RubyGems to 2.2.3 until the Windows version of Ruby has this included
  • Install eventmachine 1.0.7, a problematic gem on Windows
  • Run the Ruby Bundler's update
  • Install Middleman

And then, every deployment run the Middleman static site generator.

  • Middleman build

The first part is a one time thing for a new website. I just need to make sure Ruby is around and in the path. The second part is what runs every time a source file for my static site generator is checked in. It runs middleman build. Then at the very end, Kudu takes the results from the /build folder and moves them to /wwwroot, which makes the changes live.

Here's an annotated part of the first bit, but the actual file is on GitHub. Note that I'm putting stuff in %temp% for speed. Turns out %temp% a local drive, so it's a few times faster than using the main drive, which makes this deployment faster. However, it's cleared out often, so if I wanted things to be persistent but slower to deploy, I'd put them in D:\deployments\tools. As it is, the deploy is fast (less than a minute) when Ruby is there, and just about 3 minutes to get and setup Ruby when it's not. The exists check handles the case when a deploy happens but %temp% has been cleared so it'll get Ruby again.

NOTE: If this seems confusing or complex, it's because I like to give folks LOTS of detail. But just look at my repository. All we have is a standard "Middleman init" site plus the Azure-generator deploy.cmd and my getruby.cmd. That's all you need, plus a Basic Azure Website. The getruby.cmd is my automating what you'd have to any way on a Windows machine without Ruby.

REM Note that D:\local\temp is a LOCAL drive on Azure, and very fast
SET PATH=%PATH%;D:\local\temp\r\ruby-2.1.5-x64-mingw32\bin

pushd %temp%
REM If you need things to be persistent, then put them elsewhere, not in TEMP
if not exist r md r
cd r
if exist ruby-2.1.5-x64-mingw32 goto end

echo No Ruby, need to get it!

REM Get 64-bit Ruby
curl -o ruby215.zip http://dl.bintray.com/oneclick/rubyinstaller/ruby-2.1.5-x64-mingw32.7z?direct
ECHO START Unzipping Ruby. 7Zip is already on Azure Websites
REM Note Azure deployments run faster with 7Zip not spewing so much. Redirect to a file.
d:\7zip\7za x -y ruby215.zip > out

REM Get DevKit to build Ruby native gems
REM If you don't need DevKit for your Gems, rem this out.
curl -o DevKit.zip http://cdn.rubyinstaller.org/archives/devkits/DevKit-mingw64-64-4.7.2-20130224-1432-sfx.exe
ECHO START Unzipping DevKit
d:\7zip\7za x -y -oDevKit DevKit.zip > out
ECHO DONE Unzipping DevKit

ruby DevKit\dk.rb init

REM Tell DevKit where Ruby is
echo --- > config.yml
echo - d:/local/temp/r/ruby-2.1.5-x64-mingw32 >> config.yml

REM Setup DevKit
ruby DevKit\dk.rb install

REM Update Gem223 until someone fixes the Ruby Windows installer https://github.com/oneclick/rubyinstaller/issues/261
curl -L -o update.gem https://github.com/rubygems/rubygems/releases/download/v2.2.3/rubygems-update-2.2.3.gem
call gem install --local update.gem
call update_rubygems --no-ri --no-rdoc > updaterubygemsout
ECHO What's our new Rubygems version?
call gem --version
call gem uninstall rubygems-update -x

REM This is needed on Windows, why is this gem such a problem?
ECHO Install eventmachine 1.0.7
call gem install eventmachine -v '1.0.7' --no-ri --no-rdoc > updateventmachineout

call bundle update

ECHO Install middleman...the whole point!
call gem install middleman --no-ri --no-rdoc

:end
popd

call middleman build

REM KuduSync and actual /build to /wwwroot is after this in deploy.cmd!

And in the Deploy.cmd all I needed to change was this under SETUP. This is where YOU can do whatever you like. Note since I'm using Batch, I need to put CALL in front of other Batch files (and Ruby uses them also!) otherwise my script will just end early.

ECHO CALLING GET RUBY

call getruby.cmd

ECHO WE MADE IT

Then later, still in Deploy.cmd, I just added \build to the source directory name.

call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_SOURCE%\build" -t "%DEPLOYMENT_TARGET%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd"

And that's it.  Now whenever I updated my views or other things in my Middleman source on GitHub, it automatically deploys to my live site.

Yes, again, to be clear, I realize it's a static site generator that I could run locally and FTP the results, but I'm working in a small team and this is a great way for us to collaborate on our static site. Plus, when it's done, it's all done and I don't have to mess with it again.

Middleman Static Site Generator on Azure

Debugging Custom Azure Website Deployments

I thought debugging my GetRuby batch file was going to be a nightmare. However, it turns out that the Azure cross-platform command line (the Azure x-plat CLI, open source, and written in nodejs, BTW) can connect to Azure's log streaming service. "Azure Site Log Tail" lets me see the LIVE console output as the deploy happens!

Azure Site Log Tail

Now, note that the need for this whole "getruby.bat" file totally goes away if the Azure Websites folks start including Ruby and DevKit in the Azure Websites VM image by default. That would make Ruby, Rails, Gems, DevKit, etc. available to everyone. Do you want Ruby on Azure? Do you care? Sound off in the comments!

HELP: The batch file could use more testing, especially for robustness as well as Ruby-correctness as I'm likely confused about a few things, but it works for me and it's a great start. Sometimes different native gems don't build, though, or Gems complains about conflicting versions and asks me to run Bundler. I have no idea why. Running things twice clears it. It's either my bug or someone else's. :)

I'm just happy that Azure Websites is as flexible as it is that I was able to console into it from the browser, look around, add my own custom deployment hook, and do something I initially didn't think was possible!

Give Azure Websites a try FOR FREE, no signup, no credit card for an hour in a sandbox with PHP, Node, ASP.NET, or Java at http://try.azurewebsites.net. (Full Disclosure, I helped a little with this site, so I'm a fan.)

Related Links

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb
Tuesday, 24 February 2015 06:01:09 UTC
That's freaky.

I was just checking out middleman for use with nitrous.io (cloud based ruby IDE and VM) as I'm looking to move away from WebMatrix for my blog engine called BlogMatrix => www.kestrelblackmore.com/projects/blogmatrix.
Tuesday, 24 February 2015 09:24:34 UTC
A small typo: games -> gems

Otherwise, awesome!
Ofer
Tuesday, 24 February 2015 10:38:12 UTC
Not heard of Middlema, they have a nice looking website which is always a good sign! Is it similar to Jekyll?
Matthew Blott
Tuesday, 24 February 2015 18:27:47 UTC
Hi Scott,

Is it legal? :)
This almost enables you to use website as a VM!
Very cool!
Tuesday, 24 February 2015 21:47:23 UTC
Very cool!

I'm going to try the same thing with composer and see if I can get a Laravel app I have in Azure to do a composer update after deployment and then run my database migrations.

Thanks for the head start!
Wednesday, 25 February 2015 01:54:27 UTC
Do you know of any static site generators for Razor? I've seen a couple, but haven't found anything great.

Thanks in advance.
Hi Scott
Wednesday, 25 February 2015 11:34:21 UTC
This is wonderful!

I think a huge, nay, GINORMOUS, shout out should go to Luis Lavena, chief creator and tireless maintainer of RubyInstaller, DevKit and a number of other critical tools such as gem-compiler and rake-compiler, the super-cool work in progress DevKit-gem, plus tonnes more that have made Ruby truly great on Windows and, generally, much more cross-platform friendly. He doesn't get nearly enough credit. You should get him on Hanselminutes, Scott, he's an awesome guy doing awesome work.
Wednesday, 25 February 2015 11:42:25 UTC
BTW, yes, definitely would LOVE Ruby on Azure.
Wednesday, 25 February 2015 11:54:00 UTC
Thursday, 26 February 2015 18:19:28 UTC
What triggers execution of 'deploy.cmd'?
If I just run this in the console window, I get:

> deploy.cmd
Bad Request

Nothing seems to happen if I just publish my cloned copy of march-is-for-makers using VS2015 to Azure. Is execution of deploy.cmd somehow linked to using autopush from GitHub?
Friday, 27 February 2015 02:40:45 UTC
It would be awesome to have Ruby in the default Azure Websites VM Image.
Friday, 27 February 2015 13:53:36 UTC
Yeah it's all jolly good - but surely there are other items higher up the priority list?
Will
Friday, 27 February 2015 21:52:43 UTC
I wanted to use Azure recently to host a static site, but I had to choose AWS because I can serve static sites right out of S3 without deploying a web server at all.

Azure Storage is missing a bunch of features in this regard. You can't set a default blob document, like index.html. You have to create a container for each root folder (manually or using the API I suppose) instead of just uploading a root folder. And, among other minor features missing from Azure Storage, there is no facility to let users directly upload files.

Serving static sites that use a set of distributed APIs seems to be way that much of the new "programmable web" is being built, so Microsoft is going to have to fill in some blanks here I think.
WB
Thursday, 05 March 2015 17:46:56 UTC
I blogged about something very similar, and even easier:
http://www.ytechie.com/2013/11/blogging-awesomeness-with-a-static-generator-and-markdown/

In my case, it's as easy as cloning and deploying to Azure.

The only downside is the initial VM has to be big enough to generate the static content, and after that the capacity essentially remains idle.
Tuesday, 10 March 2015 12:05:02 UTC
Thanks Scott. That is some fun stuff I'd like to play too :)
Thursday, 12 March 2015 00:49:28 UTC
Amazing stuff! Strange coincidence as I have been looking at static site generators recently and playing with Jekyll which also runs on Ruby and seems to be very popular at the moment.

You should check out Pretzel which is a .net port of Jekyll and also supports razor syntax! So far it is working well for me and is easily installed through chocolatey (which can also be installed with a single Powershell command so I'm sure this would be possible in Azure).

When I started developing websites the first thing I worked on was an asp.net site to replace something that was statically generated and considered outdated technology. Funny how things go in cycles and come back, just somehow that bit better than before :)
Tuesday, 17 March 2015 04:50:28 UTC
If you use the Ruby Dev Kit you may need to download the sources or precompiled binaries for libraries needed by certain gems. An alternative I found recently to the Ruby Dev Kit is MSYS2. It can build native Windows applications using the MinGW-w64 toolchain. It uses the pacman package manager from Arch Linux and allows you to build packages on Windows (it can manage dependences and has the development libraries and sources you would need for most gems). For example, you can install a 64-bit version of Ruby using the command:

pacman -S mingw-w64-x86_64-ruby

You will need to force gems to build using the mingw-w64 toolchain though (by specifying (--platform=ruby (for example, gem install puma --platform=ruby)).
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.