Merge branch 'master' into ps-ember-update

Conflicts:
	Gemfile
	assets/scripts/travis.coffee
This commit is contained in:
Piotr Sarnacki 2013-03-09 12:48:35 +01:00
commit 554c8621e3
8 changed files with 116 additions and 110 deletions

View File

@ -1,6 +1,5 @@
ruby '1.9.3' rescue nil
source 'https://rubygems.org'
ruby '1.9.3'
gem 'puma'
gem 'rack-ssl', '~> 1.3'

View File

@ -15,6 +15,9 @@
<li>
<a href="http://about.travis-ci.org/docs">{{t layouts.top.docs}}</a>
</li>
<li>
<a href="http://status.travis-ci.com">{{t layouts.top.status}}</a>
</li>
<li {{bindAttr class="view.classProfile"}}>
<p class="handle">
<a class="signed-out" href="#" {{action signIn target="Travis"}}>{{t layouts.top.github_login}}</a>

View File

@ -4,6 +4,18 @@ require 'ext/ember/namespace'
window.ENV ||= {}
window.ENV.RAISE_ON_DEPRECATION = true
if window.history.state == undefined
window.history.state = {}
oldPushState = window.history.pushState
window.history.pushState = (state, title, href) ->
window.history.state = state
oldPushState.apply this, arguments
oldReplaceState = window.history.replaceState
window.history.replaceState = (state, title, href) ->
window.history.state = state
oldReplaceState.apply this, arguments
# TODO: how can I put it in Travis namespace and use immediately?
Storage = Em.Object.extend
init: ->
@ -99,7 +111,6 @@ $.extend Travis,
CONFIG_KEYS: ['rvm', 'gemfile', 'env', 'jdk', 'otp_release', 'php', 'node_js', 'perl', 'python', 'scala', 'compiler']
QUEUES: [
{ name: 'common', display: 'Common' }
{ name: 'linux', display: 'Linux' }
{ name: 'mac_osx', display: 'Mac and OSX' }
]

View File

@ -25,7 +25,7 @@ use Travis::Web::ApiRedirect do |app|
app.settings.api_endpoint = ENV['API_ENDPOINT'] if ENV['API_ENDPOINT']
end
run Travis::Web::App.new(
run Travis::Web::App.build(
environment: ENV['RACK_ENV'] || 'development',
api_endpoint: ENV['API_ENDPOINT'],
pusher_key: ENV['PUSHER_KEY'],

View File

@ -6,8 +6,11 @@ require 'delegate'
require 'time'
class Travis::Web::App
autoload :AltVersions, 'travis/web/app/alt_versions'
autoload :MobileRedirect, 'travis/web/app/mobile_redirect'
S3_URL = 'https://s3.amazonaws.com/travis-web-production/assets'
# Simple Rack router that behaves like a hash.
# Key is the path, value the response.
class Router < DelegateClass(Hash)
@ -18,103 +21,83 @@ class Travis::Web::App
end
def call(env)
if main_app.custom_branch?(env)
main_app.response_for_custom_branch(env)
else
self[env['PATH_INFO']]
end
self[env['PATH_INFO']]
end
end
def self.new(options = {})
return super unless options[:environment] == 'development'
proc { |e| super.call(e) } # poor man's reloader
class << self
def new(options = {})
return super unless options[:environment] == 'development'
proc { |e| super.call(e) } # poor man's reloader
end
def build(options = {})
builder = Rack::Builder.new
if options[:environment] == 'production'
builder.use Rack::SSL
# builder.use Rack::Cache
end
builder.use Rack::Deflater
builder.use Rack::Head
builder.use Rack::Protection::XSSHeader
builder.use Rack::Protection::FrameOptions
builder.use Rack::Protection::PathTraversal
builder.use Rack::ConditionalGet
builder.use Travis::Web::App::AltVersions
builder.run new(options)
builder.to_app
end
end
attr_reader :app, :router, :environment, :version, :last_modified, :age, :options, :root
attr_reader :routers, :version, :last_modified, :age, :options, :root
def initialize(options = {})
@options = options
@environment = options.fetch(:environment)
@root = options.fetch(:root)
@router = Router.new(self)
@app = builder.to_app
@version = File.read File.expand_path('version', root)
@last_modified = Time.now
@age = 60 * 60 * 24 * 365
load_routes
@routers = { default: create_router }
end
def call(env)
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)
name = env['travis.alt'] || :default
routers[name] ||= create_router(alt: name)
routers[name].call(env)
end
private
def disable_custom_branch?(env)
env['QUERY_STRING'] =~ /disable[_-]custom[_-]branch/
def create_router(options = {})
router = Router.new(self)
load_routes(router, options)
router
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) }
def load_routes(router, options = {})
each_file { |file| router[path_for(file)] = response_for(file, options) }
router.default = router['/']
end
def response_for(file, options = {})
content = File.read(file)
set_config(content, options) if config_needed? file
set_config(content, options) if config_needed?(file)
headers = {
'Content-Length' => content.bytesize.to_s,
'Content-Location' => route_for(file),
'Content-Location' => path_for(file),
'Cache-Control' => cache_control(file),
'Content-Location' => route_for(file),
'Content-Location' => path_for(file),
'Content-Type' => mime_type(file),
'ETag' => version,
'Last-Modified' => last_modified.httpdate,
'Expires' => (last_modified + age).httpdate,
'Vary' => vary_for(file)
}
[ 200, headers, [ content ] ]
[ 200, headers, [content] ]
end
def each_file
pattern = File.join(root, '**/*')
Dir.glob(pattern) { |f| yield f if File.file? f }
Dir.glob(File.join(root, '**/*')) { |file| yield file if File.file?(file) }
end
def prefix?(file)
@ -126,18 +109,11 @@ class Travis::Web::App
end
def index?(file)
file.end_with? 'index.html'
end
def route_for(file)
file = file.sub("#{root}/", '')
file = File.join(version, file) if prefix? file
file = "" if index? file
"/#{file}"
file.end_with?('index.html')
end
def cache_control(file)
case route_for(file)
case path_for(file)
when '/' then "public, must-revalidate"
when '/version' then "no-cache"
else "public, max-age=#{age}"
@ -145,13 +121,20 @@ class Travis::Web::App
end
def vary_for(file)
case route_for(file)
case path_for(file)
when '/' then 'Accept'
when '/version' then '*'
else ''
end
end
def path_for(file)
file = file.sub("#{root}/", '')
file = File.join(version, file) if prefix?(file)
file = "" if index?(file)
"/#{file}"
end
def mime_type(file)
Rack::Mime.mime_type File.extname(file)
end
@ -162,28 +145,7 @@ class Travis::Web::App
end
string.gsub! %r{(src|href)="(?:\/?)((styles|scripts)\/[^"]*)"} do
if opts[:custom_branch]
url = "https://s3.amazonaws.com/travis-web-production/assets/#{opts[:custom_branch]}/#{$2}"
%(#$1="#{url}")
else
%(#$1="/#{version}/#$2")
end
%(#{$1}=#{opts[:alt] ? "#{S3_URL}/#{opts[:alt]}/#{$2}" : "/#{version}/#{$2}"})
end
end
def builder
builder = Rack::Builder.new
if environment == 'production'
builder.use Rack::SSL
builder.use Rack::Cache
end
builder.use Rack::Deflater
builder.use Rack::Head
builder.use Rack::Protection::XSSHeader
builder.use Rack::Protection::FrameOptions
builder.use Rack::Protection::PathTraversal
builder.use Rack::ConditionalGet
builder.run router
builder
end
end

View File

@ -0,0 +1,29 @@
class Travis::Web::App::AltVersions
attr_reader :app
def initialize(app)
@app = app
end
def call(env)
alt = alt_from_params(env) || alt_from_cookie(env)
env['travis.alt'] = alt if alt && alt != 'default'
status, headers, body = app.call(env)
headers['Set-Cookie'] = cookie(alt) if alt
[status, headers, body]
end
private
def cookie(alt)
"alt=#{alt == 'default' ? '' : alt}; path=/; max-age=#{alt == 'default' ? 0 : 86400}"
end
def alt_from_params(env)
$1 if env['QUERY_STRING'] =~ /alt=([^&]+)/
end
def alt_from_cookie(env)
$1 if env['HTTP_COOKIE'] =~ /alt=([^;]+)/
end
end

View File

@ -96,6 +96,7 @@ en:
sign_out: Sign Out
signing_in: Signing In
stats: Stats
status: Status
locales:
ca:
de: Deutsch

View File

@ -6,7 +6,7 @@ describe Travis::Web::App do
end
describe 'catch all' do
before { get('/foo/bar') }
before { get('/foo/bar') }
example { last_response.should be_ok }
example { headers['Content-Location'].should be == '/' }
example { headers['Cache-Control'].should include('must-revalidate') }
@ -15,7 +15,7 @@ describe Travis::Web::App do
end
describe 'assets' do
before { get('/favicon.ico') }
before { get('/favicon.ico') }
example { last_response.should be_ok }
example { headers['Content-Location'].should be == '/favicon.ico' }
example { headers['Cache-Control'].should_not include('must-revalidate') }
@ -24,30 +24,31 @@ describe Travis::Web::App do
end
describe 'version' do
before { get('/version') }
before { get('/version') }
example { last_response.should be_ok }
example { headers['Content-Location'].should be == '/version' }
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') }
describe 'alternate asset versions' do
context 'not passing an alt param' do
before { get('/') }
example { headers['Set-Cookie'].should be_nil }
end
context 'passing an alt param' do
before { get('/?alt=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') }
example { headers['Set-Cookie'].should == 'alt=foo; path=/; max-age=86400' }
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=;') }
context 'passing default as an alt param' do
before { get('/?alt=default') }
example { last_response.body.should_not =~ /\/assets\/[^\/]+\/scripts\/app.js/ }
example { headers['Set-Cookie'].should == 'alt=; path=/; max-age=0' }
end
end
end