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