From aa438038117a0212f9fe84904da2899d3ced49f3 Mon Sep 17 00:00:00 2001
From: Piotr Sarnacki <drogus@gmail.com>
Date: Thu, 21 Feb 2013 22:12:33 +0100
Subject: [PATCH] Allow to set "custom_branch" for assets

When custom-branch param is set, travis-web will serve assets from S3
from given branch (the compiled assets are uploaded to S3 on each
build). Cookie will also be set to serve assets from this branch until
disabled.

Custom branch may be disabled by passing disable-custom-branch=true
param.
---
 lib/travis/web/app.rb | 64 +++++++++++++++++++++++++++++++++++++------
 spec/app_spec.rb      | 20 ++++++++++++++
 2 files changed, 76 insertions(+), 8 deletions(-)

diff --git a/lib/travis/web/app.rb b/lib/travis/web/app.rb
index f6420ac0..31466a28 100644
--- a/lib/travis/web/app.rb
+++ b/lib/travis/web/app.rb
@@ -11,12 +11,18 @@ class Travis::Web::App
   # Simple Rack router that behaves like a hash.
   # Key is the path, value the response.
   class Router < DelegateClass(Hash)
-    def initialize
+    attr_reader :main_app
+    def initialize(main_app)
+      @main_app = main_app
       super({})
     end
 
     def call(env)
-      self[env['PATH_INFO']]
+      if main_app.custom_branch?(env)
+        main_app.response_for_custom_branch(env)
+      else
+        self[env['PATH_INFO']]
+      end
     end
   end
 
@@ -31,7 +37,7 @@ class Travis::Web::App
     @options       = options
     @environment   = options.fetch(:environment)
     @root          = options.fetch(:root)
-    @router        = Router.new
+    @router        = Router.new(self)
     @app           = builder.to_app
     @version       = File.read File.expand_path('version', root)
     @last_modified = Time.now
@@ -43,16 +49,53 @@ class Travis::Web::App
     app.call(env)
   end
 
+  def response_for_custom_branch(env)
+    status, headers, body = response_for File.join(root, 'index.html'), custom_branch: custom_branch(env)
+    response = Rack::Response.new body, status, headers
+
+    if disable_custom_branch?(env)
+      response.delete_cookie 'custom_branch'
+    elsif custom_branch_from_params(env)
+      response.set_cookie 'custom_branch', value: custom_branch_from_params(env), expires: Time.now + 31536000
+    end
+
+    response.finish
+  end
+
+  def custom_branch?(env)
+    custom_branch(env) || disable_custom_branch?(env)
+  end
+
   private
 
+    def disable_custom_branch?(env)
+      env['QUERY_STRING'] =~ /disable[_-]custom[_-]branch/
+    end
+
+    def custom_branch_from_params(env)
+      branch = custom_branch_from_string env['QUERY_STRING']
+    end
+
+    def custom_branch_from_cookie(env)
+      custom_branch_from_string env['HTTP_COOKIE']
+    end
+
+    def custom_branch_from_string(string)
+      $1 if string =~ /(?<!disable.)custom[_-]branch=([^&]+)/
+    end
+
+    def custom_branch(env)
+      custom_branch_from_params(env) || custom_branch_from_cookie(env)
+    end
+
     def load_routes
       each_file { |f| router[route_for(f)] = response_for(f) }
       router.default = router['/']
     end
 
-    def response_for(file)
+    def response_for(file, options = {})
       content = File.read(file)
-      set_config(content) if config_needed? file
+      set_config(content, options) if config_needed? file
 
       headers = {
         'Content-Length'   => content.bytesize.to_s,
@@ -113,13 +156,18 @@ class Travis::Web::App
       Rack::Mime.mime_type File.extname(file)
     end
 
-    def set_config(string)
+    def set_config(string, options = {})
       string.gsub! %r(<meta (rel|name)="travis\.([^"]*)" (href|value)="([^"]*)"[^>]*>) do
         %(<meta #{$1}="travis.#{$2}" #{$3}="#{options[$2.to_sym] || $4}">)
       end
 
-      string.gsub! %r{(src|href)="(\/?)((styles|scripts)\/[^"]*)"} do
-        %(#$1="#$2#{version}/#$3")
+      string.gsub! %r{(src|href)="(?:\/?)((styles|scripts)\/[^"]*)"} do
+        if options[:custom_branch]
+          url = "https://s3.amazonaws.com/travis-web-production/assets/#{options[:custom_branch]}/#{$2}"
+          %(#$1="#{url}")
+        else
+          %(#$1="/#{version}/#$2")
+        end
       end
     end
 
diff --git a/spec/app_spec.rb b/spec/app_spec.rb
index 3b4f4698..584616ba 100644
--- a/spec/app_spec.rb
+++ b/spec/app_spec.rb
@@ -30,4 +30,24 @@ describe Travis::Web::App do
     example { headers['Cache-Control'].should be == 'no-cache' }
     example { headers['Vary'].split(',').should_not include('Accept') }
   end
+
+  describe 'custom branch' do
+    context 'when passing custom branch as a param' do
+      before { get('/?custom-branch=foo') }
+      example { last_response.should be_ok }
+      example { last_response.body.should include('/assets/foo/styles/app.css') }
+      example { last_response.body.should include('/assets/foo/scripts/app.js') }
+      example { headers['Set-Cookie'].should include('custom_branch=foo') }
+    end
+
+    context 'disabling custom branch' do
+      before { get('/?disable-custom-branch=true') }
+      example { last_response.should be_ok }
+      example { last_response.body.should =~ %r{src="/[^\/]+/scripts/app.js} }
+      example { last_response.body.should_not include('/assets/true/styles/app.css') }
+      example { last_response.body.should_not include('/assets/foo/styles/app.css') }
+      example { last_response.body.should_not include('/assets/foo/scripts/app.js') }
+      example { headers['Set-Cookie'].should include('custom_branch=;') }
+    end
+  end
 end