How To Automate Optimization and Deployment Of Static Content

FTP-Upload

You’re on the home stretch. Now you have all improved files together and need to upload them to your host. Again, here’s a little Ruby script that does exactly that for you.

 require 'net/ftp'

ftp_host = ARGV[0]
ftp_user = ARGV[1]
ftp_password = ARGV[2]

localfile = ARGV[3] #e.g. "../js/static.min.js.gz"
remote_dir = ARGV[4] #e.g. "www/js"

ftp = Net::FTP.open(ftp_host, ftp_user, ftp_password) 

puts "FTP - Status: #{ftp.status}."

puts "FTP - Go to directory: #{remote_dir}."
ftp.chdir(remote_dir)
puts "FTP - Uploading file: #{localfile}..."
ftp.putbinaryfile(localfile)

files = ftp.list puts "FTP - Your file was uploaded here:" puts files

ftp.close 

Save it as upload.rb, put in the credentials of your host, eventually change the destination directory and then call it like that:

ruby ftp.rb example.org admin mysecret ../js/static.min.js www/js

Which stands for:
ruby ftp.rb ftp-host ftp-user ftp-password file-to-upload ftp-destination-folder

The minified Javascript file should be uploaded now. Repeat this for the CSS file and the gzipped versions of both.

Please note that the folders on the ftp server, where the files are copied to, must already exist.

Putting it all together

You got all the pieces of this puzzle and now you can finally put them together, by grouping the commands in a batch file. On Windows it would look like this:

@echo off

echo -------- MERGING JS FILES --------

call juicer merge -i --force ../js/static.js 

echo -------- FINISHED MERGING JS FILES --------

echo -------- MERGING CSS FILES --------

call juicer merge --force ../css/static.css

echo -------- FINISHED MERGING CSS FILES --------

echo -------- COMPRESSING FILES --------

ruby gzip.rb ../js/static.min.js

ruby gzip.rb ../css/static.min.css

echo -------- FINSISHED COMPRESSING FILES --------

echo -------- UPLOADING FILES --------

ruby ftp.rb example.org admin mysecret ../js/static.min.js www/js

ruby ftp.rb example.org admin mysecret ../js/static.min.js.gz www/js

ruby ftp.rb example.org admin mysecret ../css/static.min.css www/css

ruby ftp.rb example.org admin mysecret ../css/static.min.css.gz www/css

echo -------- FINISHED UPLOADING FILES --------

Save it in a file called e.g. batch.bat, insert your FTP credentials and call it like this:

cd deploy //switch to subdirectory
batch.bat

On OS X you can do it like that:

#!/bin/bash

echo -------- MERGING JS FILES --------

juicer merge -i --force ../js/static.js 

echo -------- FINISHED MERGING JS FILES --------

echo -------- MERGING CSS FILES --------

juicer merge --force ../css/static.css

echo -------- FINISHED MERGING CSS FILES --------

echo -------- COMPRESSING FILES --------

ruby gzip.rb ../js/static.min.js

ruby gzip.rb ../css/static.min.css

echo -------- FINSISHED COMPRESSING FILES --------

echo -------- UPLOADING FILES --------

ruby ftp.rb example.org admin mysecret ../js/static.min.js www/js

ruby ftp.rb example.org admin mysecret ../js/static.min.js.gz www/js

ruby ftp.rb example.org admin mysecret ../css/static.min.css www/css

ruby ftp.rb example.org admin mysecret ../css/static.min.css.gz www/css

echo -------- FINISHED UPLOADING FILES --------

Save it in a file called e.g. batch, insert your FTP credentials and call it like this:

 cd deploy //switch to subdirectory sh batch 

And the content gets merged, minified, compressed and uploaded in one rush.
Additionally to the CSS and Javascript files, it would be a good idea, if the graphics file for the CSS sprites would be uploaded too? Just add an additional call of the upload script in the batch file. It can be used with any filetype.

4. In your template

Further development and bug fixing is hard to do in a minified file. But you can add an if-clause in your templates to vary the referencing of your files, depending on your environment.

Take the original files for your local development and the minified for your online version. Additionally you should check if the users browser supports gzipped content (most modern browsers do).

An example for PHP:

if($_SERVER["SERVER_NAME"] == "localhost") {
	//local development server - load all files
} else {
	if (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')){
		//production system - client accepts gzip
	} else {
		//production system - client does not accept gzip
	}
}

And an example for Ruby on Rails:

<% if RAILS_ENV == 'development' %>
  <%# local development server - load all files %>
<% else %>
  <% if self.request.env['HTTP_ACCEPT_ENCODING'].match(/gzip/) %>
    <%# production system - client accepts gzip %>
  <% else %>
    <%# production system - client does not accept gzip %>
  <% end %>
<% end %>

5. Get even faster with subdomains

Like mentioned before, most browsers only download two files per host simultaneously. You can bypass that rule by creating some subdomains for your static content and referencing the files through them.

For example, create a subdomain on your web hosting account, that’s called img.example.org, which points to example.org/img.

Do this for all of your folders, containing static content and use the subdomains to reference your resources:

img.example.org/sprites.jpg
css.example.org/static.min.css
js.example.org/static.min.js

This way, the rule won’t be applied and all files can be downloaded at the same time.

6. Conclusion

If you got all of this working for one of your projects, you surely recognized the advantages for you as a developer and for the users as the consumers of your content.

These examples don’t claim to be perfect. But if you didn’t approach to automate recurring tasks in your daily workflow as a developer, you hopefully have an idea now, how it could look like.

Always keep in mind, that computers were invented to spare you the boredom of repeating, simple tasks. So save your time for more important things.

Of course, this doesn’t have to be the end of the line. If you’re interested in the automatic deployment of whole applications, you should have a look at Capistrano. Again, it’s written in Ruby, but you can also use it for PHP platforms.

Further Resources

-->