Merge branch 'master' into rkh-metrics
Conflicts: Gemfile.lock
|
@ -2,6 +2,7 @@ language: ruby
|
|||
rvm:
|
||||
- 1.9.3
|
||||
- rbx-19mode
|
||||
- jruby-19mode
|
||||
before_script:
|
||||
- 'RAILS_ENV=test rake db:create db:schema:load --trace'
|
||||
notifications:
|
||||
|
@ -9,3 +10,4 @@ notifications:
|
|||
matrix:
|
||||
allow_failures:
|
||||
- rvm: rbx-19mode
|
||||
- rvm: jruby-19mode
|
||||
|
|
15
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Contributing to Travis-CI
|
||||
Issues for any Travis-CI repo should be submitted to https://github.com/travis-ci/travis-ci/issues
|
||||
|
||||
## Security Issues
|
||||
***Any security issues should be submitted directly to [security@travis-ci.org](mailto:security@travis-ci.org)***
|
||||
|
||||
## Reporting Issues
|
||||
- Explain what you expected to happen vs the actual results
|
||||
- Include a screenshot if it helps illustrate the issue. https://github.com/blog/1347-issue-attachments
|
||||
- What steps are required to reproduce the issue
|
||||
- An example build that shows the issue
|
||||
|
||||
## Submitting a PR to Travis-API
|
||||
|
||||
See testing and setup notes in the base [README](https://github.com/travis-ci/travis-api)
|
17
Gemfile
|
@ -1,18 +1,18 @@
|
|||
ruby '1.9.3' rescue nil
|
||||
|
||||
source :rubygems
|
||||
source 'https://rubygems.org'
|
||||
gemspec
|
||||
|
||||
gem 'travis-support', github: 'travis-ci/travis-support'
|
||||
gem 'travis-core', github: 'travis-ci/travis-core'
|
||||
gem 'travis-support', github: 'travis-ci/travis-support'
|
||||
gem 'travis-sidekiqs', github: 'travis-ci/travis-sidekiqs', require: nil, ref: 'cde9741'
|
||||
gem 'sinatra', github: 'sinatra/sinatra'
|
||||
gem 'sinatra-contrib', github: 'sinatra/sinatra-contrib', require: nil
|
||||
gem 'sinatra' #github: 'sinatra/sinatra'
|
||||
gem 'sinatra-contrib', require: nil #github: 'sinatra/sinatra-contrib', require: nil
|
||||
|
||||
# TODO need to release the gem as soon i'm certain this change makes sense
|
||||
gem 'simple_states', github: 'svenfuchs/simple_states', branch: 'sf-set-state-early'
|
||||
|
||||
gem 'unicorn'
|
||||
gem 'puma', '1.6.3'
|
||||
gem "sentry-raven", github: 'getsentry/raven-ruby'
|
||||
gem 'yard-sinatra', github: 'rkh/yard-sinatra'
|
||||
gem 'rack-contrib', github: 'rack/rack-contrib'
|
||||
|
@ -21,7 +21,9 @@ gem 'gh', github: 'rkh/gh'
|
|||
gem 'bunny'
|
||||
gem 'dalli'
|
||||
gem 'pry'
|
||||
gem 'metriks', '0.9.9.2'
|
||||
gem 'metriks', '0.9.9.2'
|
||||
|
||||
gem 'ar-octopus', github: 'travis-ci/octopus', require: nil
|
||||
|
||||
group :test do
|
||||
gem 'rspec', '~> 2.11'
|
||||
|
@ -34,9 +36,10 @@ group :development do
|
|||
gem 'foreman'
|
||||
gem 'rerun'
|
||||
# gem 'debugger'
|
||||
gem 'rb-fsevent', '~> 0.9.1'
|
||||
end
|
||||
|
||||
group :development, :test do
|
||||
gem 'rake', '~> 0.9.2'
|
||||
gem 'micro_migrations', git: 'http://gist.github.com/4269321.git'
|
||||
gem 'micro_migrations', git: 'https://gist.github.com/4269321.git'
|
||||
end
|
||||
|
|
237
Gemfile.lock
|
@ -1,10 +1,10 @@
|
|||
GIT
|
||||
remote: git://github.com/getsentry/raven-ruby.git
|
||||
revision: a0f59d3974034b38891fb1d4a70ff45fa996e7c9
|
||||
revision: 267b33417a3ed43f552911cf353561a641ba9fb2
|
||||
specs:
|
||||
sentry-raven (0.4.0)
|
||||
sentry-raven (0.4.6)
|
||||
faraday (>= 0.7.6)
|
||||
hashie
|
||||
hashie (>= 1.1.0)
|
||||
multi_json (~> 1.0)
|
||||
uuidtools
|
||||
|
||||
|
@ -17,11 +17,11 @@ GIT
|
|||
|
||||
GIT
|
||||
remote: git://github.com/rkh/gh.git
|
||||
revision: 1dece05c588c63e714520aae686589de1b3bcbd5
|
||||
revision: ff93c759591a66c9d5250cada5234d2adde95dd3
|
||||
specs:
|
||||
gh (0.9.1)
|
||||
gh (0.11.1)
|
||||
addressable
|
||||
backports (~> 2.3)
|
||||
backports
|
||||
faraday (~> 0.8)
|
||||
multi_json (~> 1.0)
|
||||
net-http-persistent (>= 2.7)
|
||||
|
@ -29,32 +29,11 @@ GIT
|
|||
|
||||
GIT
|
||||
remote: git://github.com/rkh/yard-sinatra.git
|
||||
revision: 3b1064eef407d2d288a5b96d258178a1e67b3b80
|
||||
revision: e61831bca0431b35eaa62fdd18acbc65f81322af
|
||||
specs:
|
||||
yard-sinatra (1.0.0)
|
||||
yard (~> 0.7)
|
||||
|
||||
GIT
|
||||
remote: git://github.com/sinatra/sinatra-contrib.git
|
||||
revision: 86c85007860bbaf596092547e7902ff5e0a07698
|
||||
specs:
|
||||
sinatra-contrib (1.4.0)
|
||||
backports (>= 2.0)
|
||||
eventmachine
|
||||
rack-protection
|
||||
rack-test
|
||||
sinatra (~> 1.4.0)
|
||||
tilt (~> 1.3)
|
||||
|
||||
GIT
|
||||
remote: git://github.com/sinatra/sinatra.git
|
||||
revision: 459369eb66224836f72e21bbece58c007f3422fa
|
||||
specs:
|
||||
sinatra (1.4.0)
|
||||
rack (~> 1.4)
|
||||
rack-protection (~> 1.3)
|
||||
tilt (~> 1.3, >= 1.3.3)
|
||||
|
||||
GIT
|
||||
remote: git://github.com/svenfuchs/simple_states.git
|
||||
revision: b1d45144e6a758220d7b21f83b08dc92de0d3196
|
||||
|
@ -64,23 +43,30 @@ GIT
|
|||
activesupport
|
||||
hashr (~> 0.0.10)
|
||||
|
||||
GIT
|
||||
remote: git://github.com/travis-ci/octopus.git
|
||||
revision: 2d4cca475479516f47c3144971205f50c335ad35
|
||||
specs:
|
||||
ar-octopus (0.5.0beta)
|
||||
activerecord (>= 2.3.0)
|
||||
activesupport (>= 2.3.0)
|
||||
|
||||
GIT
|
||||
remote: git://github.com/travis-ci/travis-core.git
|
||||
revision: 259e48ffc68a67eff32848334025ef17ab58a3b3
|
||||
revision: 9ecc95058bd63f9f70799c7f9f8ad41cbd39b16c
|
||||
specs:
|
||||
travis-core (0.0.1)
|
||||
actionmailer (~> 3.2.11)
|
||||
activerecord (~> 3.2.11)
|
||||
actionmailer (~> 3.2.12)
|
||||
activerecord (~> 3.2.12)
|
||||
coder (~> 0.3.0)
|
||||
data_migrations (~> 0.0.1)
|
||||
gh
|
||||
hashr (~> 0.0.19)
|
||||
metriks (~> 0.9.7)
|
||||
multi_json
|
||||
postmark-rails (~> 0.4.1)
|
||||
pusher (~> 0.11.0)
|
||||
railties (~> 3.2.11)
|
||||
rake (~> 0.9.2.2)
|
||||
railties (~> 3.2.12)
|
||||
rake
|
||||
redis (~> 3.0)
|
||||
rollout (~> 1.1.0)
|
||||
simple_states (~> 0.1.1)
|
||||
|
@ -96,12 +82,12 @@ GIT
|
|||
|
||||
GIT
|
||||
remote: git://github.com/travis-ci/travis-support.git
|
||||
revision: cf916e10949db43ce6f2b6f86082b367f04acfcd
|
||||
revision: 5463f10e58563e79b950da8c6c392d1e80ec3013
|
||||
specs:
|
||||
travis-support (0.0.1)
|
||||
|
||||
GIT
|
||||
remote: http://gist.github.com/4269321.git
|
||||
remote: https://gist.github.com/4269321.git
|
||||
revision: 8e2d21b924a69dd48191df6a18e51769f5a88614
|
||||
specs:
|
||||
micro_migrations (0.0.1)
|
||||
|
@ -112,10 +98,10 @@ PATH
|
|||
travis-api (0.0.1)
|
||||
backports (~> 2.5)
|
||||
hubble (~> 0.1)
|
||||
newrelic_rpm (~> 3.5.0)
|
||||
newrelic_rpm (~> 3.6.1.88)
|
||||
pg (~> 0.13.2)
|
||||
rack-contrib (~> 1.1)
|
||||
rack-ssl (~> 1.3)
|
||||
rack-ssl (~> 1.3, >= 1.3.3)
|
||||
redcarpet (~> 2.1)
|
||||
sinatra (~> 1.3)
|
||||
sinatra-contrib (~> 1.3)
|
||||
|
@ -124,73 +110,78 @@ PATH
|
|||
travis-support
|
||||
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actionmailer (3.2.11)
|
||||
actionpack (= 3.2.11)
|
||||
mail (~> 2.4.4)
|
||||
actionpack (3.2.11)
|
||||
activemodel (= 3.2.11)
|
||||
activesupport (= 3.2.11)
|
||||
actionmailer (3.2.13)
|
||||
actionpack (= 3.2.13)
|
||||
mail (~> 2.5.3)
|
||||
actionpack (3.2.13)
|
||||
activemodel (= 3.2.13)
|
||||
activesupport (= 3.2.13)
|
||||
builder (~> 3.0.0)
|
||||
erubis (~> 2.7.0)
|
||||
journey (~> 1.0.4)
|
||||
rack (~> 1.4.0)
|
||||
rack (~> 1.4.5)
|
||||
rack-cache (~> 1.2)
|
||||
rack-test (~> 0.6.1)
|
||||
sprockets (~> 2.2.1)
|
||||
activemodel (3.2.11)
|
||||
activesupport (= 3.2.11)
|
||||
activemodel (3.2.13)
|
||||
activesupport (= 3.2.13)
|
||||
builder (~> 3.0.0)
|
||||
activerecord (3.2.11)
|
||||
activemodel (= 3.2.11)
|
||||
activesupport (= 3.2.11)
|
||||
activerecord (3.2.13)
|
||||
activemodel (= 3.2.13)
|
||||
activesupport (= 3.2.13)
|
||||
arel (~> 3.0.2)
|
||||
tzinfo (~> 0.3.29)
|
||||
activesupport (3.2.11)
|
||||
i18n (~> 0.6)
|
||||
activesupport (3.2.13)
|
||||
i18n (= 0.6.1)
|
||||
multi_json (~> 1.0)
|
||||
addressable (2.3.2)
|
||||
addressable (2.3.4)
|
||||
arel (3.0.2)
|
||||
atomic (1.0.1)
|
||||
atomic (1.1.9)
|
||||
avl_tree (1.1.3)
|
||||
backports (2.7.1)
|
||||
backports (2.8.2)
|
||||
builder (3.0.4)
|
||||
bunny (0.8.0)
|
||||
celluloid (0.12.4)
|
||||
facter (>= 1.6.12)
|
||||
timers (>= 1.0.0)
|
||||
coder (0.3.0)
|
||||
coderay (1.0.8)
|
||||
coderay (1.0.9)
|
||||
connection_pool (0.9.3)
|
||||
daemons (1.1.9)
|
||||
dalli (2.6.0)
|
||||
dalli (2.6.3)
|
||||
data_migrations (0.0.1)
|
||||
activerecord
|
||||
rake
|
||||
database_cleaner (0.8.0)
|
||||
diff-lcs (1.1.3)
|
||||
diff-lcs (1.2.4)
|
||||
dotenv (0.7.0)
|
||||
erubis (2.7.0)
|
||||
eventmachine (1.0.0)
|
||||
facter (1.6.17)
|
||||
eventmachine (1.0.3)
|
||||
facter (1.7.0)
|
||||
factory_girl (2.4.2)
|
||||
activesupport
|
||||
faraday (0.8.4)
|
||||
faraday (0.8.7)
|
||||
multipart-post (~> 1.1)
|
||||
foreman (0.61.0)
|
||||
ffi (1.8.1)
|
||||
foreman (0.63.0)
|
||||
dotenv (>= 0.7)
|
||||
thor (>= 0.13.6)
|
||||
hashie (1.2.0)
|
||||
hashie (2.0.4)
|
||||
hashr (0.0.22)
|
||||
hike (1.2.1)
|
||||
hitimes (1.1.1)
|
||||
hike (1.2.2)
|
||||
hitimes (1.2.1)
|
||||
hubble (0.1.2)
|
||||
yajl-ruby (~> 1.1)
|
||||
i18n (0.6.1)
|
||||
journey (1.0.4)
|
||||
json (1.7.6)
|
||||
kgio (2.8.0)
|
||||
listen (0.7.2)
|
||||
mail (2.4.4)
|
||||
json (1.7.7)
|
||||
listen (1.0.3)
|
||||
rb-fsevent (>= 0.9.3)
|
||||
rb-inotify (>= 0.9)
|
||||
rb-kqueue (>= 0.2)
|
||||
mail (2.5.3)
|
||||
i18n (>= 0.4.0)
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
|
@ -200,101 +191,108 @@ GEM
|
|||
atomic (~> 1.0)
|
||||
avl_tree (~> 1.1.2)
|
||||
hitimes (~> 1.1)
|
||||
mime-types (1.19)
|
||||
mocha (0.13.1)
|
||||
mime-types (1.23)
|
||||
mocha (0.13.3)
|
||||
metaclass (~> 0.0.1)
|
||||
multi_json (1.5.0)
|
||||
multipart-post (1.1.5)
|
||||
multi_json (1.7.3)
|
||||
multipart-post (1.2.0)
|
||||
net-http-persistent (2.8)
|
||||
net-http-pipeline (1.0.1)
|
||||
newrelic_rpm (3.5.5.38)
|
||||
newrelic_rpm (3.6.1.88)
|
||||
pg (0.13.2)
|
||||
polyglot (0.3.3)
|
||||
postmark (0.9.18)
|
||||
json
|
||||
rake
|
||||
postmark-rails (0.4.1)
|
||||
actionmailer
|
||||
postmark (>= 0.9.0)
|
||||
rake
|
||||
pry (0.9.11.4)
|
||||
pry (0.9.12.1)
|
||||
coderay (~> 1.0.5)
|
||||
method_source (~> 0.8)
|
||||
slop (~> 3.4)
|
||||
pusher (0.11.2)
|
||||
puma (1.6.3)
|
||||
rack (~> 1.2)
|
||||
pusher (0.11.3)
|
||||
multi_json (~> 1.0)
|
||||
signature (~> 0.1.6)
|
||||
rack (1.4.4)
|
||||
rack (1.4.5)
|
||||
rack-cache (1.2)
|
||||
rack (>= 0.4)
|
||||
rack-protection (1.3.2)
|
||||
rack-protection (1.5.0)
|
||||
rack
|
||||
rack-ssl (1.3.2)
|
||||
rack-ssl (1.3.3)
|
||||
rack
|
||||
rack-test (0.6.2)
|
||||
rack (>= 1.0)
|
||||
railties (3.2.11)
|
||||
actionpack (= 3.2.11)
|
||||
activesupport (= 3.2.11)
|
||||
railties (3.2.13)
|
||||
actionpack (= 3.2.13)
|
||||
activesupport (= 3.2.13)
|
||||
rack-ssl (~> 1.3.2)
|
||||
rake (>= 0.8.7)
|
||||
rdoc (~> 3.4)
|
||||
thor (>= 0.14.6, < 2.0)
|
||||
raindrops (0.10.0)
|
||||
rake (0.9.2.2)
|
||||
rdoc (3.12)
|
||||
rake (0.9.6)
|
||||
rb-fsevent (0.9.3)
|
||||
rb-inotify (0.9.0)
|
||||
ffi (>= 0.5.0)
|
||||
rb-kqueue (0.2.0)
|
||||
ffi (>= 0.5.0)
|
||||
rdoc (3.12.2)
|
||||
json (~> 1.4)
|
||||
redcarpet (2.2.2)
|
||||
redis (3.0.2)
|
||||
redis-namespace (1.2.1)
|
||||
redis (3.0.4)
|
||||
redis-namespace (1.3.0)
|
||||
redis (~> 3.0.0)
|
||||
rerun (0.7.1)
|
||||
listen
|
||||
rerun (0.8.1)
|
||||
listen (>= 1.0.3)
|
||||
rollout (1.1.0)
|
||||
rspec (2.12.0)
|
||||
rspec-core (~> 2.12.0)
|
||||
rspec-expectations (~> 2.12.0)
|
||||
rspec-mocks (~> 2.12.0)
|
||||
rspec-core (2.12.2)
|
||||
rspec-expectations (2.12.1)
|
||||
diff-lcs (~> 1.1.3)
|
||||
rspec-mocks (2.12.1)
|
||||
rspec (2.13.0)
|
||||
rspec-core (~> 2.13.0)
|
||||
rspec-expectations (~> 2.13.0)
|
||||
rspec-mocks (~> 2.13.0)
|
||||
rspec-core (2.13.1)
|
||||
rspec-expectations (2.13.0)
|
||||
diff-lcs (>= 1.1.3, < 2.0)
|
||||
rspec-mocks (2.13.1)
|
||||
sidekiq (2.5.4)
|
||||
celluloid (~> 0.12.0)
|
||||
connection_pool (~> 0.9.2)
|
||||
multi_json (~> 1)
|
||||
redis (~> 3)
|
||||
redis-namespace
|
||||
signature (0.1.6)
|
||||
slop (3.4.3)
|
||||
signature (0.1.7)
|
||||
sinatra (1.3.6)
|
||||
rack (~> 1.4)
|
||||
rack-protection (~> 1.3)
|
||||
tilt (~> 1.3, >= 1.3.3)
|
||||
sinatra-contrib (1.3.2)
|
||||
backports (>= 2.0)
|
||||
eventmachine
|
||||
rack-protection
|
||||
rack-test
|
||||
sinatra (~> 1.3.0)
|
||||
tilt (~> 1.3)
|
||||
slop (3.4.4)
|
||||
sprockets (2.2.2)
|
||||
hike (~> 1.2)
|
||||
multi_json (~> 1.0)
|
||||
rack (~> 1.0)
|
||||
tilt (~> 1.1, != 1.3.0)
|
||||
thin (1.5.0)
|
||||
thin (1.5.1)
|
||||
daemons (>= 1.0.9)
|
||||
eventmachine (>= 0.12.6)
|
||||
rack (>= 1.0.0)
|
||||
thor (0.14.6)
|
||||
tilt (1.3.3)
|
||||
tilt (1.4.1)
|
||||
timers (1.1.0)
|
||||
treetop (1.4.12)
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
tzinfo (0.3.35)
|
||||
unicorn (4.5.0)
|
||||
kgio (~> 2.6)
|
||||
rack
|
||||
raindrops (~> 0.7)
|
||||
uuidtools (2.1.3)
|
||||
tzinfo (0.3.37)
|
||||
uuidtools (2.1.4)
|
||||
yajl-ruby (1.1.0)
|
||||
yard (0.8.3)
|
||||
yard (0.8.6.1)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
ar-octopus!
|
||||
bunny
|
||||
dalli
|
||||
database_cleaner (~> 0.8.0)
|
||||
|
@ -305,18 +303,19 @@ DEPENDENCIES
|
|||
micro_migrations!
|
||||
mocha (~> 0.12)
|
||||
pry
|
||||
puma (= 1.6.3)
|
||||
rack-cache (~> 1.2)
|
||||
rack-contrib!
|
||||
rake (~> 0.9.2)
|
||||
rb-fsevent (~> 0.9.1)
|
||||
rerun
|
||||
rspec (~> 2.11)
|
||||
sentry-raven!
|
||||
simple_states!
|
||||
sinatra!
|
||||
sinatra-contrib!
|
||||
sinatra
|
||||
sinatra-contrib
|
||||
travis-api!
|
||||
travis-core!
|
||||
travis-sidekiqs!
|
||||
travis-support!
|
||||
unicorn
|
||||
yard-sinatra!
|
||||
|
|
9
Rakefile
|
@ -1,7 +1,11 @@
|
|||
require 'bundler/setup'
|
||||
ENV['SCHEMA'] = "#{Gem.loaded_specs['travis-core'].full_gem_path}/db/schema.rb"
|
||||
|
||||
require 'micro_migrations'
|
||||
begin
|
||||
require 'micro_migrations'
|
||||
rescue LoadError
|
||||
# we can't load micro migrations on production
|
||||
end
|
||||
require 'travis'
|
||||
|
||||
begin
|
||||
|
@ -33,3 +37,6 @@ task 'travis-api.gemspec' do
|
|||
end
|
||||
|
||||
task default: 'travis-api.gemspec'
|
||||
|
||||
tasks_path = File.expand_path('../lib/tasks/*.rake', __FILE__)
|
||||
Dir.glob(tasks_path).each { |r| import r }
|
||||
|
|
12
config.ru
|
@ -7,10 +7,12 @@ $stdout.sync = true
|
|||
require 'travis/api/app'
|
||||
require 'core_ext/module/load_constants'
|
||||
|
||||
# models = Travis::Model.constants.map(&:to_s)
|
||||
# only = [/^(ActiveRecord|ActiveModel|Travis|GH|#{models.join('|')})/]
|
||||
# [Travis::Api, Travis, GH].each do |target|
|
||||
# target.load_constants! :only => only, :skip => ['Travis::Memory', 'GH::ResponseWrapper'], :debug => false
|
||||
# end
|
||||
models = Travis::Model.constants.map(&:to_s)
|
||||
only = [/^(ActiveRecord|ActiveModel|Travis|GH|#{models.join('|')})/]
|
||||
skip = ['Travis::Memory', 'GH::ResponseWrapper', 'Travis::NewRelic']
|
||||
|
||||
[Travis::Api, Travis, GH].each do |target|
|
||||
target.load_constants! :only => only, :skip => skip, :debug => false
|
||||
end
|
||||
|
||||
run Travis::Api::App.new
|
||||
|
|
23
lib/tasks/build_update_branch.rake
Normal file
|
@ -0,0 +1,23 @@
|
|||
namespace :build do
|
||||
namespace :migrate do
|
||||
task :branch do
|
||||
require 'travis'
|
||||
Travis::Database.connect
|
||||
|
||||
|
||||
Build.select(['id', 'commit_id']).pushes.includes(:commit).find_in_batches do |builds|
|
||||
branches = Hash.new { |h, k| h[k] = [] }
|
||||
|
||||
builds.each do |build|
|
||||
#next if build.branch
|
||||
branches[build.commit.branch] << build.id
|
||||
end
|
||||
|
||||
branches.each do |branch, ids|
|
||||
Build.where(id: ids).update_all(branch: branch)
|
||||
end
|
||||
end; nil
|
||||
|
||||
end
|
||||
end
|
||||
end
|
22
lib/tasks/build_update_pull_request_data.rake
Normal file
|
@ -0,0 +1,22 @@
|
|||
namespace :build do
|
||||
namespace :migrate do
|
||||
task :pull_request_data do
|
||||
require 'travis'
|
||||
Travis::Database.connect
|
||||
|
||||
Build.pull_requests.includes(:request).order('id DESC').find_in_batches do |builds|
|
||||
Build.transaction do
|
||||
builds.each do |build|
|
||||
next if build.pull_request_number && build.pull_request_title
|
||||
attrs = {
|
||||
:pull_request_number => build.request.pull_request_number,
|
||||
:pull_request_title => build.request.pull_request_title
|
||||
}
|
||||
|
||||
Build.where(id: build.id).update_all(attrs)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -52,7 +52,11 @@ module Travis::Api
|
|||
end
|
||||
|
||||
def self.deploy_sha
|
||||
@deploy_sha ||= File.exist?('.deploy_sha') ? File.read('.deploy-sha')[0..7] : 'deploy-sha'
|
||||
@deploy_sha ||= File.exist?(deploy_sha_path) ? File.read(deploy_sha_path)[0..7] : 'deploy-sha'
|
||||
end
|
||||
|
||||
def self.deploy_sha_path
|
||||
File.expand_path('../../../../.deploy-sha', __FILE__)
|
||||
end
|
||||
|
||||
attr_accessor :app
|
||||
|
@ -70,8 +74,8 @@ module Travis::Api
|
|||
if Travis::Features.feature_active?(:use_rack_cache) && memcache_server
|
||||
use Rack::Cache,
|
||||
verbose: true,
|
||||
metastore: "memcached://#{memcache_servers}/#{self.class.deploy_sha}",
|
||||
entitystore: "memcached://#{memcache_servers}/#{self.class.deploy_sha}"
|
||||
metastore: "memcached://#{memcache_servers}/#{Travis::Api::App.deploy_sha}",
|
||||
entitystore: "memcached://#{memcache_servers}/#{Travis::Api::App.deploy_sha}"
|
||||
end
|
||||
|
||||
use Rack::Deflater
|
||||
|
@ -100,6 +104,10 @@ module Travis::Api
|
|||
|
||||
private
|
||||
|
||||
def self.console?
|
||||
defined? Travis::Console
|
||||
end
|
||||
|
||||
def self.setup!
|
||||
setup_travis
|
||||
load_endpoints
|
||||
|
@ -109,18 +117,50 @@ module Travis::Api
|
|||
|
||||
def self.setup_travis
|
||||
Travis::Amqp.config = Travis.config.amqp
|
||||
Travis::Database.connect
|
||||
|
||||
setup_database_connections
|
||||
|
||||
Travis::Features.start
|
||||
Sidekiq.configure_client do |config|
|
||||
config.redis = Travis.config.redis.merge(size: 1, namespace: Travis.config.sidekiq.namespace)
|
||||
|
||||
if Travis.env == 'production' || Travis.env == 'staging'
|
||||
Sidekiq.configure_client do |config|
|
||||
config.redis = Travis.config.redis.merge(size: 1, namespace: Travis.config.sidekiq.namespace)
|
||||
end
|
||||
end
|
||||
|
||||
Raven.configure do |config|
|
||||
config.dsn = Travis.config.sentry.dsn
|
||||
end if Travis.config.sentry
|
||||
if Travis.env == 'production' and not console?
|
||||
Raven.configure do |config|
|
||||
config.dsn = Travis.config.sentry.dsn
|
||||
end if Travis.config.sentry
|
||||
|
||||
Travis::LogSubscriber::ActiveRecordMetrics.attach
|
||||
$metriks_reporter = Metriks::Reporter::Logger.new
|
||||
Travis::LogSubscriber::ActiveRecordMetrics.attach
|
||||
Travis::Notification.setup
|
||||
end
|
||||
end
|
||||
|
||||
def self.setup_database_connections
|
||||
Travis::Database.connect
|
||||
|
||||
return unless Travis.config.use_database_follower?
|
||||
require 'octopus'
|
||||
|
||||
if Travis.env == 'production' || Travis.env == 'staging'
|
||||
puts "Setting up the DB follower as a read slave"
|
||||
|
||||
# Octopus checks for Rails.env, just hardcode enabled?
|
||||
Octopus.instance_eval do
|
||||
def enabled?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
ActiveRecord::Base.custom_octopus_connection = false
|
||||
|
||||
::Octopus.setup do |config|
|
||||
config.shards = { :follower => Travis.config.database_follower }
|
||||
config.environments = ['production', 'staging']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.load_endpoints
|
||||
|
|
|
@ -4,7 +4,7 @@ require 'securerandom'
|
|||
class Travis::Api::App
|
||||
class AccessToken
|
||||
DEFAULT_SCOPES = [:public, :private]
|
||||
attr_reader :token, :scopes, :user_id, :app_id
|
||||
attr_reader :token, :scopes, :user_id, :app_id, :expires_in, :extra
|
||||
|
||||
def self.create(options = {})
|
||||
new(options).tap(&:save)
|
||||
|
@ -18,25 +18,40 @@ class Travis::Api::App
|
|||
def self.find_by_token(token)
|
||||
return token if token.is_a? self
|
||||
user_id, app_id, *scopes = redis.lrange(key(token), 0, -1)
|
||||
new(token: token, scopes: scopes, user_id: user_id, app_id: app_id) if user_id
|
||||
extra = decode_json(scopes.pop) if scopes.last && scopes.last =~ /^json:/
|
||||
new(token: token, scopes: scopes, user_id: user_id, app_id: app_id, extra: extra) if user_id
|
||||
end
|
||||
|
||||
def initialize(options = {})
|
||||
raise ArgumentError, 'must supply either user_id or user' unless options.key?(:user) ^ options.key?(:user_id)
|
||||
raise ArgumentError, 'must supply app_id' unless options.key?(:app_id)
|
||||
|
||||
begin
|
||||
@expires_in = Integer(options[:expires_in]) if options[:expires_in]
|
||||
rescue ArgumentError
|
||||
raise ArgumentError, 'expires_in must be of integer type'
|
||||
end
|
||||
|
||||
@app_id = Integer(options[:app_id])
|
||||
@scopes = Array(options[:scopes] || options[:scope] || DEFAULT_SCOPES).map(&:to_sym)
|
||||
@user = options[:user]
|
||||
@user_id = Integer(options[:user_id] || @user.id)
|
||||
@token = options[:token] || reuse_token || SecureRandom.urlsafe_base64(16)
|
||||
@extra = options[:extra]
|
||||
end
|
||||
|
||||
def save
|
||||
key = key(token)
|
||||
redis.del(key)
|
||||
redis.rpush(key, [user_id, app_id, *scopes].map(&:to_s))
|
||||
data = [user_id, app_id, *scopes]
|
||||
data << encode_json(extra) if extra
|
||||
redis.rpush(key, data.map(&:to_s))
|
||||
redis.set(reuse_key, token)
|
||||
|
||||
if expires_in
|
||||
redis.expire(reuse_key, expires_in)
|
||||
redis.expire(key, expires_in)
|
||||
end
|
||||
end
|
||||
|
||||
def user
|
||||
|
@ -60,6 +75,14 @@ class Travis::Api::App
|
|||
def key(token)
|
||||
"t:#{token}"
|
||||
end
|
||||
|
||||
def encode_json(hash)
|
||||
'json:' + Base64.encode64(hash.to_json)
|
||||
end
|
||||
|
||||
def decode_json(json)
|
||||
JSON.parse(Base64.decode64(json.gsub(/^json:/, '')))
|
||||
end
|
||||
end
|
||||
|
||||
include Helpers
|
||||
|
@ -68,7 +91,7 @@ class Travis::Api::App
|
|||
private
|
||||
|
||||
def reuse_token
|
||||
redis.get(reuse_key)
|
||||
redis.get(reuse_key) unless expires_in
|
||||
end
|
||||
|
||||
def reuse_key
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
require 'travis/api/app'
|
||||
require 'sinatra/base'
|
||||
require 'new_relic/agent/instrumentation/rack'
|
||||
|
||||
class Travis::Api::App
|
||||
# Superclass for any endpoint and middleware.
|
||||
|
@ -18,6 +17,17 @@ class Travis::Api::App
|
|||
"This feature has not yet been implemented. Sorry :(\n\nPull Requests welcome!"
|
||||
end
|
||||
|
||||
# hotfix??
|
||||
def route_missing
|
||||
@app ? forward : halt(404)
|
||||
end
|
||||
|
||||
def call(env)
|
||||
super
|
||||
rescue Sinatra::NotFound
|
||||
[404, {'Content-Type' => 'text/plain'}, ['Tell Konstantin to fix this!']]
|
||||
end
|
||||
|
||||
configure do
|
||||
# We pull in certain protection middleware in App.
|
||||
# Being token based makes us invulnerable to common
|
||||
|
|
|
@ -10,7 +10,7 @@ class Travis::Api::App
|
|||
set(:prefix) { "/" << name[/[^:]+$/].underscore }
|
||||
set disable_root_endpoint: false
|
||||
register :scoping
|
||||
helpers :current_user, :flash
|
||||
helpers :current_user, :flash, :db_follower
|
||||
|
||||
# TODO hmmm?
|
||||
before { flash.clear }
|
||||
|
|
|
@ -4,10 +4,12 @@ class Travis::Api::App
|
|||
class Endpoint
|
||||
# Artifacts are generated by builds. Currently we only expose logs as
|
||||
# artifacts
|
||||
#
|
||||
# **DEPRECATED** will be removed as soon as the client uses /logs/:id
|
||||
class Artifacts < Endpoint
|
||||
# Fetches an artifact by it's *id*.
|
||||
get '/:id' do |id|
|
||||
respond_with service(:find_artifact, params)
|
||||
respond_with service(:find_log, params)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -218,7 +218,7 @@ class Travis::Api::App
|
|||
end
|
||||
|
||||
def user_for_github_token(token, drop_token = false)
|
||||
data = GH.with(token: token.to_s) { GH['user'] }
|
||||
data = GH.with(token: token.to_s, client_id: nil) { GH['user'] }
|
||||
scopes = parse_scopes data.headers['x-oauth-scopes']
|
||||
halt 403, 'insufficient access: %p' unless acceptable? scopes
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@ class Travis::Api::App
|
|||
class Endpoint
|
||||
class Builds < Endpoint
|
||||
get '/' do
|
||||
respond_with service(:find_builds, params)
|
||||
name = params[:branches] ? :find_branches : :find_builds
|
||||
respond_with service(name, params)
|
||||
end
|
||||
|
||||
get '/:id' do
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require 'travis/api/app'
|
||||
require 'travis/api/app/endpoint/documentation/resources'
|
||||
|
||||
class Travis::Api::App
|
||||
class Endpoint
|
||||
|
@ -44,6 +45,7 @@ class Travis::Api::App
|
|||
|
||||
def with_code_highlighting(str)
|
||||
str.
|
||||
gsub(/json\(:([^)]+)\)/) { "<pre>" + Resources::Helpers.json($1) + "</pre>" }.
|
||||
gsub('<pre', '<pre class="prettyprint linenums pre-scrollable"').
|
||||
gsub(/<\/?code>/, '').
|
||||
gsub(/TODO:?/, '<span class="label label-warning">TODO</span>')
|
||||
|
@ -81,154 +83,116 @@ __END__
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta charset="utf-8">
|
||||
<title>Travis API documentation</title>
|
||||
|
||||
<!-- we might wanna change this -->
|
||||
<link href="<%= url('/css/bootstrap.css') %>" rel="stylesheet" />
|
||||
<link href="<%= url('/css/prettify.css') %>" rel="stylesheet" />
|
||||
<script src="<%= url('/js/jquery.js') %>"></script>
|
||||
<script src="<%= url('/js/prettify.js') %>"></script>
|
||||
<script src="<%= url('/js/bootstrap.min.js') %>"></script>
|
||||
|
||||
<style type="text/css">
|
||||
header {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
margin-top: 36px;
|
||||
}
|
||||
header h1 {
|
||||
text-shadow: 2px 2px 5px #000;
|
||||
margin-bottom: 9px;
|
||||
font-size: 81px;
|
||||
font-weight: bold;
|
||||
letter-spacing: -1px;
|
||||
line-height: 1;
|
||||
}
|
||||
header p {
|
||||
margin-bottom: 18px;
|
||||
font-weight: 300;
|
||||
font-size: 18px;
|
||||
}
|
||||
.page-header {
|
||||
margin-top: 90px;
|
||||
}
|
||||
.route {
|
||||
margin-bottom: 36px;
|
||||
}
|
||||
.page-header a {
|
||||
color: black;
|
||||
}
|
||||
.nav-list a {
|
||||
color: inherit !important;
|
||||
}
|
||||
</style>
|
||||
<!-- <link href="<%= url('/css/bootstrap.css') %>" rel="stylesheet" /> -->
|
||||
<!-- <link href="<%= url('/css/prettify.css') %>" rel="stylesheet" /> -->
|
||||
<link href="<%= url('/css/style.css') %>" rel="stylesheet" />
|
||||
<!-- <script src="<%= url('/js/jquery.js') %>"></script> -->
|
||||
<!-- <script src="<%= url('/js/prettify.js') %>"></script> -->
|
||||
<!-- <script src="<%= url('/js/bootstrap.min.js') %>"></script> -->
|
||||
</head>
|
||||
|
||||
<body onload="prettyPrint()">
|
||||
|
||||
<a href="https://github.com/travis-ci/travis-api">
|
||||
<img style="position: absolute; top: 0; right: 0; border: 0;"
|
||||
src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png"
|
||||
alt="Fork me on GitHub">
|
||||
</a>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<header class="span12">
|
||||
<h1>The Travis API</h1>
|
||||
<p>All the routes, just waiting for you to build something awesome.</p>
|
||||
</header>
|
||||
<div id="navigation">
|
||||
<div class="wrapper">
|
||||
<a href="http://travis-ci.org" id="logo">travis-ci<span>.org</span></a>
|
||||
<ul>
|
||||
<li><a href="http://about.travis-ci.org/blog/">Blog</a></li>
|
||||
<li><a href="http://about.travis-ci.org/docs/">User Documentation</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div id="header">
|
||||
<div class="wrapper">
|
||||
<h1 class="riddle"><a href="/docs" title="Travis API">The Travis API</a></h1>
|
||||
<p>All the routes, just waiting for you to build something awesome.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside class="span3">
|
||||
<div class="page-header">
|
||||
<h1>Navigation</h1>
|
||||
<div id="content">
|
||||
<div class="wrapper">
|
||||
<div class="pad">
|
||||
<div id="main">
|
||||
<% general_docs.each do |doc| %>
|
||||
<%= erb :entry, locals: doc %>
|
||||
<% end %>
|
||||
|
||||
<% endpoints.each do |endpoint| %>
|
||||
<%= erb :entry, {},
|
||||
id: endpoint['name'],
|
||||
title: endpoint['name'],
|
||||
content: erb(:endpoint_content, {}, endpoint: endpoint) %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="well" style="padding: 8px 0;">
|
||||
<ul class="nav nav-list">
|
||||
<% general_docs.each do |doc| %>
|
||||
<li class="nav-header"><a href="#<%= doc[:id] %>"><%= doc[:title] %></a></li>
|
||||
<% doc[:subheaders].each do |sub| %>
|
||||
<li><a href="#<%= sub %>"><%= sub %></a></li>
|
||||
<% end %>
|
||||
<div id="sidebar">
|
||||
<% general_docs.each do |doc| %>
|
||||
<h2><a href="#<%= doc[:id] %>"><%= doc[:title] %></a></h2>
|
||||
<ul>
|
||||
<% doc[:subheaders].each do |sub| %>
|
||||
<li><a href="#<%= sub %>"><%= sub %></a></li>
|
||||
<% end %>
|
||||
<li class="divider"></li>
|
||||
<% endpoints.each do |endpoint| %>
|
||||
<li class="nav-header"><a href="#<%= endpoint['name'] %>"><%= endpoint['name'] %></a></li>
|
||||
<% endpoint['routes'].each do |route| %>
|
||||
<li>
|
||||
<a href="#<%= slug_for(route) %>">
|
||||
<i class="icon-<%= icon_for route['verb'] %>"></i>
|
||||
<tt><%= route['uri'] %></tt>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% end %>
|
||||
|
||||
<% endpoints.each do |endpoint| %>
|
||||
<h2><a href="#<%= endpoint['name'] %>"><%= endpoint['name'] %></a></h2>
|
||||
<ul>
|
||||
<% endpoint['routes'].each do |route| %>
|
||||
<li>
|
||||
<a href="#<%= slug_for(route) %>">
|
||||
<i class="icon-<%= icon_for route['verb'] %>"></i>
|
||||
<tt><%= route['uri'] %></tt>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
<li class="divider"></li>
|
||||
<li class="nav-header">
|
||||
External Links
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://travis-ci.org">
|
||||
<i class="icon-globe"></i>
|
||||
Travis CI
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/travis-ci/travis-api">
|
||||
<i class="icon-cog"></i>
|
||||
Source Code
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/travis-ci/travis-api/issues">
|
||||
<i class="icon-list-alt"></i>
|
||||
API issues
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/travis-ci/travis-ember">
|
||||
<i class="icon-play-circle"></i>
|
||||
Example Client
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<% end %>
|
||||
|
||||
<h2>External Links</h2>
|
||||
<ul>
|
||||
<li><a href="https://travis-ci.org">Travis CI</a></li>
|
||||
<li><a href="https://github.com/travis-ci/travis-api">Source Code</a></li>
|
||||
<li><a href="https://github.com/travis-ci/travis-api/issues">API issues</a></li>
|
||||
<li><a href="https://github.com/travis-ci/travis-web">Example Client</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<section class="span9">
|
||||
|
||||
<% general_docs.each do |doc| %>
|
||||
<%= erb :entry, locals: doc %>
|
||||
<% end %>
|
||||
|
||||
<% endpoints.each do |endpoint| %>
|
||||
<%= erb :entry, {},
|
||||
id: endpoint['name'],
|
||||
title: endpoint['name'],
|
||||
content: erb(:endpoint_content, {}, endpoint: endpoint) %>
|
||||
<% end %>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div class="wrapper">
|
||||
<div class="box">
|
||||
<p>This site is maintained by the <a href="http://github.com/travis-ci">Travis CI community</a>. Feel free to <a href="http://github.com/travis-ci/travis-api">contribute</a>!</p>
|
||||
</div>
|
||||
<div class="box">
|
||||
<p>This design was kindly provided by the talented Ben Webster of <a href="http://www.plus2.com.au">Plus2</a>.</p>
|
||||
</div>
|
||||
<div class="box last">
|
||||
<ul>
|
||||
<li><a href="https://github.com/travis-ci" title="">Travis CI on GitHub</a></li>
|
||||
<li><a href="https://twitter.com/travisci" title="">Travis CI on Twitter</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ endpoint_content
|
||||
<% unless endpoint['doc'].to_s.empty? %>
|
||||
<%= docs_for endpoint %>
|
||||
<hr>
|
||||
<% end %>
|
||||
<% endpoint['routes'].each do |route| %>
|
||||
<div class="route" id="<%= slug_for(route) %>">
|
||||
<pre><h3><%= route['verb'] %> <%= route['uri'] %></h3></pre>
|
||||
<h3><%= route['verb'] %> <%= route['uri'] %></h3>
|
||||
<% if route['scope'] %>
|
||||
<p>
|
||||
<h5>Required autorization scope: <span class="label"><%= route['scope'] %></span></h5>
|
||||
<h5>Required authorization scope: <span class="label"><%= route['scope'] %></span></h5>
|
||||
</p>
|
||||
<% end %>
|
||||
<%= docs_for route %>
|
||||
|
@ -237,11 +201,7 @@ __END__
|
|||
|
||||
@@ entry
|
||||
<div id="<%= id %>">
|
||||
<div class="page-header">
|
||||
<h1>
|
||||
<a href="#<%= id %>"><%= title %></a>
|
||||
</h1>
|
||||
</div>
|
||||
<h2><%= title %> <a class="toc-anchor" href="#<%= id %>">#</a></h2>
|
||||
<%= content %>
|
||||
</div>
|
||||
|
||||
|
|
594
lib/travis/api/app/endpoint/documentation/css/style.css
Normal file
|
@ -0,0 +1,594 @@
|
|||
/* ---( = begin global reset thanks to eric meyer elements )------------------------------- */
|
||||
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, font, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
font-weight: inherit;
|
||||
font-style: inherit;
|
||||
font-size: 100%;
|
||||
font-family: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/* remember to define focus styles! */
|
||||
:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
line-height: 1;
|
||||
color: black;
|
||||
background: white;
|
||||
}
|
||||
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
/* tables still need 'cellspacing="0"' in the markup */
|
||||
table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
caption, th, td {
|
||||
text-align: left;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
blockquote:before, blockquote:after, q:before, q:after {
|
||||
content: "";
|
||||
}
|
||||
|
||||
blockquote, q {
|
||||
quotes: "" "";
|
||||
}
|
||||
|
||||
/* travis-ci styles */
|
||||
|
||||
body {
|
||||
margin: 0 0 1em 0;
|
||||
font-size: 14px;
|
||||
line-height: 1.4286;
|
||||
color: #555;
|
||||
background: #fff;
|
||||
font-family: "Helvetica Neue", Arial, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #3366cc;
|
||||
outline: none;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #666;
|
||||
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #66cc33;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
p, ul, blockquote, pre, td, th, label {
|
||||
margin: 1.4286em 0;
|
||||
font-size: 1em;
|
||||
line-height: 1.4286;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
font-style: italic;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
blockquote small.author {
|
||||
font-style: normal;
|
||||
margin-top: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
blockquote small.author:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
p small.author {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin: 1.4286em 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
li {
|
||||
line-height: 1.4286;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-family: Helvetica, Arial;
|
||||
color: #8e7a2b;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
em {
|
||||
font-style: italic;
|
||||
}
|
||||
span.help {
|
||||
font-style: italic;
|
||||
background-color: #ffff99;
|
||||
font-family: Georgia, Times, Serif;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-top: 1em;
|
||||
padding: 1em 1.5em;
|
||||
line-height: 1.5em;
|
||||
border: 1px solid #ddd;
|
||||
background: #fafafa;
|
||||
border-bottom-left-radius: 8px 8px;
|
||||
border-bottom-right-radius: 8px 8px;
|
||||
border-top-left-radius: 8px 8px;
|
||||
border-top-right-radius: 8px 8px;
|
||||
font-family: monospace;
|
||||
font-size: 13px;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
p > code, div > code, li > code {
|
||||
background-color: #fafafa;
|
||||
border: 1px solid #e0e0e0;
|
||||
color: #333;
|
||||
padding: 0px 0.2em;
|
||||
font-family: monospace;
|
||||
font-size: 13.3px;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
width: 960px;
|
||||
margin: 0 auto;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div#navigation {
|
||||
clear: both;
|
||||
margin: 0 0 0 0;
|
||||
padding: .8em 0 .6em 0;
|
||||
overflow: hidden;
|
||||
background: #efefef;
|
||||
color: #888;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div#header {
|
||||
clear: both;
|
||||
margin: 1em auto 0 auto;
|
||||
padding-top: 10px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div#navigation .wrapper {
|
||||
width: 940px;
|
||||
}
|
||||
|
||||
div#navigation .wrapper a#logo {
|
||||
color: #828282;
|
||||
display:block;
|
||||
float: left;
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
line-height: 1.2em;
|
||||
margin: 0.15em 1em 0 0;
|
||||
padding: 0 1em 0 0;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
div#navigation .wrapper a#logo span {
|
||||
color: #bbbaba;
|
||||
font-size: .85em;
|
||||
}
|
||||
|
||||
div#navigation ul {
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
div#navigation ul li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
float: left;
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div#navigation ul li.right {
|
||||
float: right;
|
||||
border-left: none;
|
||||
font-size: .83em;
|
||||
}
|
||||
|
||||
div#navigation ul li a,
|
||||
div#navigation ul li a:visited {
|
||||
color: #999;
|
||||
display:block;
|
||||
padding: 0em 1em 0 1em ;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
div#navigation ul li a:hover,
|
||||
div#navigation ul li a:visited {
|
||||
text-decoration:underline;
|
||||
}
|
||||
|
||||
div#navigation ul li.selected a,
|
||||
div#navigation ul li.selected a:visited {
|
||||
text-decoration: none;
|
||||
color: #6c3;
|
||||
}
|
||||
|
||||
div#navigation ul li.active a,
|
||||
div#navigation ul li.selected a:visited {
|
||||
text-decoration: none;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
div#content {
|
||||
clear: both;
|
||||
margin: 0 auto 3em auto;
|
||||
padding: 0 0 0;
|
||||
text-align:center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div#content div.pad {
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
div#main {
|
||||
float:left;
|
||||
width: 580px;
|
||||
overflow: hidden;
|
||||
padding-top: 2em;
|
||||
}
|
||||
|
||||
#main ul,
|
||||
#main ul li {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
#main ol,
|
||||
#main ol li {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
#main li {
|
||||
margin-left: 1.4286em;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
#main figure {
|
||||
clear: both !important;
|
||||
width: 578px;
|
||||
border: 1px solid #efefef;
|
||||
text-align: center;
|
||||
margin: 0px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#main figure figcaption {
|
||||
background-color: #efefef;
|
||||
text-align: left;
|
||||
font-size: 80%;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
#main figure img {
|
||||
max-width: 570px;
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
#main figure.left {
|
||||
margin-bottom: 1.4286em;
|
||||
margin-right: 1.4286em;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#main figure.right {
|
||||
margin-bottom: 1.4286em;
|
||||
margin-left: 1.4286em;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#main figure.smallest {
|
||||
width: 138px;
|
||||
}
|
||||
|
||||
#main figure.smallest img {
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
|
||||
#main figure.smaller {
|
||||
width: 208px;
|
||||
}
|
||||
|
||||
#main figure.smaller img {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
#main figure.small {
|
||||
width: 258px;
|
||||
}
|
||||
|
||||
#main figure.small img {
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
div#sidebar {
|
||||
float:right;
|
||||
width: 325px;
|
||||
overflow: hidden;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
#sidebar ul {
|
||||
margin:0 0 1.5em 0;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
#sidebar p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#sidebar p,
|
||||
#sidebar ul li p,
|
||||
#sidebar ul li a,
|
||||
#sidebar ul li a:visited {
|
||||
display:block;
|
||||
text-decoration:none;
|
||||
padding:4px 0 3px 20px;
|
||||
border-top: 1px solid #efefef;
|
||||
}
|
||||
|
||||
#sidebar ul li a:hover,
|
||||
#sidebar ul li a:visited:hover {
|
||||
text-decoration:underline;
|
||||
}
|
||||
|
||||
#sidebar ul li.selected a,
|
||||
#sidebar ul li.selected a:visited {
|
||||
color: #6c3;
|
||||
background: #efefef;
|
||||
margin-left: 0;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
#sidebar ul li ul {
|
||||
border-bottom: 0;
|
||||
list-style-type: disc;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
#sidebar ul li ul li {
|
||||
border-bottom: 0;
|
||||
list-style-type: disc;
|
||||
margin-left: 1.5em;
|
||||
}
|
||||
|
||||
#sidebar ul li ul li a,
|
||||
#sidebar ul li ul li a:visited,
|
||||
#sidebar ul li.selected ul li a,
|
||||
#sidebar ul li.selected ul li a:visited {
|
||||
display:block;
|
||||
text-decoration:none;
|
||||
padding:0;
|
||||
border-top: 0;
|
||||
font-weight: normal;
|
||||
color: #195190;
|
||||
}
|
||||
|
||||
#sidebar ul li ul li.selected a,
|
||||
#sidebar ul li ul li.selected a:visited {
|
||||
color: #444;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
div#footer {
|
||||
clear: both;
|
||||
margin: 0 auto;
|
||||
padding-top: 10px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
background: #efefef;
|
||||
border-top: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd
|
||||
}
|
||||
|
||||
div#footer .wrapper {
|
||||
background: transparent;
|
||||
width: 920px;
|
||||
}
|
||||
|
||||
div#footer div.box {
|
||||
float: left;
|
||||
width: 280px;
|
||||
margin-right: 30px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
div#footer div.last {
|
||||
margin-right: 0;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
div#footer div.box ul li a,
|
||||
div#footer div.box ul li a:visited {
|
||||
color: #7d8997;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: bold;
|
||||
color: #40454F;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3em;
|
||||
line-height: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.6em;
|
||||
margin-bottom: 1em;
|
||||
line-height: 1em;
|
||||
letter-spacing: -0.5px;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.meta {
|
||||
margin-top: -1.7em;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.15em;
|
||||
line-height: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 0.95em;
|
||||
line-height: 1.25;
|
||||
margin-bottom: 1.25em;
|
||||
height: 1.25em;
|
||||
}
|
||||
|
||||
h1 img, h2 img, h3 img, h4 img, h5 img, h6 img {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1 a,
|
||||
h1 a:visited,
|
||||
h2 a,
|
||||
h2 a:visited,
|
||||
h3 a,
|
||||
h3 a:visited,
|
||||
h4 a,
|
||||
h4 a:visited,
|
||||
h5 a,
|
||||
h5 a:visited {
|
||||
color: #464755;
|
||||
}
|
||||
|
||||
div#header h1 {
|
||||
float: left;
|
||||
width: 350px;
|
||||
text-indent: -20000em;
|
||||
background: transparent;
|
||||
font-size:1px;
|
||||
margin: 20px 0 0 20px;
|
||||
}
|
||||
|
||||
div#header h1 a,
|
||||
div#header h1 a:visited,
|
||||
div#header h1 a:hover,
|
||||
div#header h1 a:visited:hover {
|
||||
display: block;
|
||||
width: 350px;
|
||||
height: 75px;
|
||||
}
|
||||
|
||||
div#header h1.riddle {
|
||||
float: none;
|
||||
width: 100%;
|
||||
text-indent: 0;
|
||||
background: #fff;
|
||||
color: #000;
|
||||
font-size:4em;
|
||||
margin: 20px 0 0 0;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
div#header h1.riddle a,
|
||||
div#header h1.riddle a:visited,
|
||||
div#header h1.riddle a:hover,
|
||||
div#header h1.riddle a:visited:hover {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
div#header p {
|
||||
color: #777;
|
||||
display: block;
|
||||
font: italic 1.25em Georgia, Times, Serif;
|
||||
line-height: 1.67em;
|
||||
margin: 0.935em 0 1.87em 0;
|
||||
padding: 0 0 1.25em 0;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div#sidebar h2 {
|
||||
font-size: .9em;
|
||||
letter-spacing: .5px;
|
||||
text-transform:uppercase;
|
||||
margin: 40px 20px 5px 20px;
|
||||
}
|
||||
|
||||
.highlight pre {
|
||||
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
|
||||
padding: 0 1.5em;
|
||||
}
|
||||
|
||||
.highlight br {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
background-color: #f8f8f8;
|
||||
border: 1px solid silver;
|
||||
font-family: 'Courier New', 'Terminal', monospace;
|
||||
color: #100;
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
|
||||
div#content div.pad {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
div#navigation ul li.lang {
|
||||
float: right;
|
||||
border-left: none;
|
||||
font-size: .83em;
|
||||
}
|
||||
|
||||
#dsq-global-toolbar ul li {
|
||||
list-style-type: none;
|
||||
}
|
141
lib/travis/api/app/endpoint/documentation/resources.rb
Normal file
|
@ -0,0 +1,141 @@
|
|||
require 'json'
|
||||
|
||||
class Travis::Api::App::Endpoint
|
||||
module Resources
|
||||
module Helpers
|
||||
def self.json(key)
|
||||
JSON.pretty_generate(Resources.const_get(key.to_s.upcase))
|
||||
end
|
||||
end
|
||||
|
||||
REPOSITORY_KEY = {
|
||||
'public_key' => '-----BEGIN RSA PUBLIC KEY-----\nMIGJAoGBAOcx131amMqIzm5+FbZz+DhIgSDbFzjKKpzaN5UWVCrLSc57z64xxTV6\nkaOTZmjCWz6WpaPkFZY+czfL7lmuZ/Y6UNm0vupvdZ6t27SytFFGd1/RJlAe89tu\nGcIrC1vtEvQu2frMLvHqFylnGd5Gy64qkQT4KRhMsfZctX4z5VzTAgMBAAE=\n-----END RSA PUBLIC KEY-----\n',
|
||||
}
|
||||
|
||||
REPOSITORY = {
|
||||
'repo' => {
|
||||
'id' => 119756,
|
||||
'slug' => 'travis-ci/travis-api',
|
||||
'description' => 'The public Travis API',
|
||||
'last_build_id' => 6347735,
|
||||
'last_build_number' => '468',
|
||||
'last_build_state' => 'started',
|
||||
'last_build_duration' => nil,
|
||||
'last_build_language' => nil,
|
||||
'last_build_started_at' => '2013-04-15T09:45:29Z',
|
||||
'last_build_finished_at' => nil,
|
||||
}
|
||||
}
|
||||
|
||||
REPOSITORIES = { 'repos' => [ REPOSITORY['repo'] ] }
|
||||
|
||||
SHORT_BUILD = {
|
||||
'id' => 6347735,
|
||||
'repository_id' => 119756,
|
||||
'commit_id' => 1873023,
|
||||
'number' => '468',
|
||||
'pull_request' => false,
|
||||
'pull_request_title' => nil,
|
||||
'pull_request_number' => nil,
|
||||
'config' => {
|
||||
'language' => 'ruby',
|
||||
'rvm' => [
|
||||
'1.9.3',
|
||||
'rbx-19mode',
|
||||
'jruby-19mode',
|
||||
],
|
||||
'before_script' => [
|
||||
'RAILS_ENV=test rake db:create db:schema:load --trace',
|
||||
],
|
||||
'notifications' => {
|
||||
'irc' => 'irc.freenode.org#travis',
|
||||
},
|
||||
'matrix' => {
|
||||
'allow_failures' => [
|
||||
{
|
||||
'rvm' => 'rbx-19mode',
|
||||
},
|
||||
{
|
||||
'rvm' => 'jruby-19mode',
|
||||
},
|
||||
],
|
||||
},
|
||||
'.result' => 'configured',
|
||||
},
|
||||
'state' => 'passed',
|
||||
'started_at' => '2013-04-15T09:45:29Z',
|
||||
'finished_at' => '2013-04-15T09:49:42Z',
|
||||
'duration' => 489,
|
||||
'job_ids' => [
|
||||
6347736,
|
||||
6347737,
|
||||
6347738,
|
||||
],
|
||||
}
|
||||
|
||||
COMMIT = {
|
||||
'id' => 1873023,
|
||||
'sha' => 'a18f211f6f921affd1ecd8c18691b40d9948aae5',
|
||||
'branch' => 'master',
|
||||
'message' => "Merge pull request #25 from henrikhodne/add-responses-to-documentation\n\nAdd responses to documentation",
|
||||
'committed_at' => '2013-04-15T09:44:31Z',
|
||||
'author_name' => 'Henrik Hodne',
|
||||
'author_email' => 'me@henrikhodne.com',
|
||||
'committer_name' => 'Henrik Hodne',
|
||||
'committer_email' => 'me@henrikhodne.com',
|
||||
'compare_url' => 'https://github.com/travis-ci/travis-api/compare/0f31ff4fb6aa...a18f211f6f92',
|
||||
'pull_request_number' => nil,
|
||||
}
|
||||
|
||||
BUILDS = {
|
||||
'builds' => [
|
||||
SHORT_BUILD
|
||||
],
|
||||
'commits' => [
|
||||
COMMIT
|
||||
]
|
||||
}
|
||||
|
||||
JOB = {
|
||||
'id' => 6347736,
|
||||
'repository_id' => 119756,
|
||||
'build_id' => 6347735,
|
||||
'commit_id' => 1873023,
|
||||
'log_id' => 1219815,
|
||||
'state' => 'passed',
|
||||
'number' => '468.1',
|
||||
'config' => {
|
||||
'language' => 'ruby',
|
||||
'rvm' => '1.9.3',
|
||||
'before_script' => [
|
||||
'RAILS_ENV=test rake db:create db:schema:load --trace',
|
||||
],
|
||||
'notifications' => {
|
||||
'irc' => 'irc.freenode.org#travis',
|
||||
},
|
||||
'matrix' => {
|
||||
'allow_failures' => [
|
||||
{
|
||||
'rvm' => 'rbx-19mode',
|
||||
},
|
||||
{
|
||||
'rvm' => 'jruby-19mode',
|
||||
}
|
||||
]
|
||||
},
|
||||
'.result' => 'configured'
|
||||
},
|
||||
'started_at' => '2013-04-15T09:45:29Z',
|
||||
'finished_at' => '2013-04-15T09:48:14Z',
|
||||
'queue' => 'builds.linux',
|
||||
'allow_failure' => false,
|
||||
'tags' => '',
|
||||
}
|
||||
|
||||
BUILD = {
|
||||
'build' => SHORT_BUILD,
|
||||
'commit' => COMMIT,
|
||||
'jobs' => [ JOB ]
|
||||
}
|
||||
end
|
||||
end
|
|
@ -10,6 +10,31 @@ class Travis::Api::App
|
|||
get '/:id' do
|
||||
respond_with service(:find_job, params)
|
||||
end
|
||||
|
||||
get '/:job_id/log' do
|
||||
resource = service(:find_log, params).run
|
||||
if !resource || resource.archived?
|
||||
archived_log_path = archive_url("/jobs/#{params[:job_id]}/log.txt")
|
||||
|
||||
if params[:cors_hax]
|
||||
status 204
|
||||
headers['Access-Control-Expose-Headers'] = 'Location'
|
||||
headers['Location'] = archived_log_path
|
||||
else
|
||||
redirect archived_log_path, 307
|
||||
end
|
||||
else
|
||||
respond_with resource
|
||||
end
|
||||
end
|
||||
|
||||
def archive_url(path)
|
||||
"https://s3.amazonaws.com/#{hostname('archive')}#{path}"
|
||||
end
|
||||
|
||||
def hostname(name)
|
||||
"#{name}#{'-staging' if Travis.env == 'staging'}.#{Travis.config.host.split('.')[-2, 2].join('.')}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
21
lib/travis/api/app/endpoint/logs.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
class Endpoint
|
||||
# Logs are generated by builds.
|
||||
class Logs < Endpoint
|
||||
# Fetches a log by it's *id*.
|
||||
get '/:id' do |id|
|
||||
respond_with service(:find_log, params)
|
||||
end
|
||||
|
||||
put '/:id' do |id|
|
||||
# TODO @rkh ... rather lost in the auth/scopes code.
|
||||
token = env['HTTP_TOKEN']
|
||||
halt 403, 'no token' unless token
|
||||
halt 403, 'internal' unless token == Travis.config.tokens.internal
|
||||
respond_with service(:update_log, params)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -8,10 +8,19 @@ class Travis::Api::App
|
|||
# You can filter the repositories by adding parameters to the request. For example, you can get all repositories
|
||||
# owned by johndoe by adding `owner_name=johndoe`, or all repositories that johndoe has access to by adding
|
||||
# `member=johndoe`. The parameter names correspond to the keys of the response hash.
|
||||
#
|
||||
# ### Response
|
||||
#
|
||||
# json(:repositories)
|
||||
get '/' do
|
||||
respond_with service(:find_repos, params)
|
||||
end
|
||||
|
||||
# Gets the repository with the given id.
|
||||
#
|
||||
# ### Response
|
||||
#
|
||||
# json(:repository)
|
||||
get '/:id' do
|
||||
respond_with service(:find_repo, params)
|
||||
end
|
||||
|
@ -20,6 +29,15 @@ class Travis::Api::App
|
|||
respond_with service(:find_repo, params.merge(schema: 'cc'))
|
||||
end
|
||||
|
||||
# Get the public key for the repository with the given id.
|
||||
#
|
||||
# This can be used to encrypt secure variables in the build configuration. See
|
||||
# [the encryption keys](http://about.travis-ci.org/docs/user/encryption-keys/) documentation page for more
|
||||
# information.
|
||||
#
|
||||
# ### Response
|
||||
#
|
||||
# json(:repository_key)
|
||||
get '/:id/key' do
|
||||
respond_with service(:find_repo_key, params), version: :v2
|
||||
end
|
||||
|
@ -28,14 +46,31 @@ class Travis::Api::App
|
|||
respond_with service(:regenerate_repo_key, params), version: :v2
|
||||
end
|
||||
|
||||
# Gets the repository with the given name.
|
||||
#
|
||||
# ### Response
|
||||
#
|
||||
# json(:repository)
|
||||
get '/:owner_name/:name' do
|
||||
respond_with service(:find_repo, params)
|
||||
prefer_follower do
|
||||
respond_with service(:find_repo, params)
|
||||
end
|
||||
end
|
||||
|
||||
# Gets the builds for the repository with the given name.
|
||||
#
|
||||
# ### Response
|
||||
#
|
||||
# json(:builds)
|
||||
get '/:owner_name/:name/builds' do
|
||||
respond_with service(:find_builds, params)
|
||||
end
|
||||
|
||||
# Get a build with the given id in the repository with the given name.
|
||||
#
|
||||
# ### Response
|
||||
#
|
||||
# json(:build)
|
||||
get '/:owner_name/:name/builds/:id' do
|
||||
respond_with service(:find_build, params)
|
||||
end
|
||||
|
@ -44,6 +79,15 @@ class Travis::Api::App
|
|||
respond_with service(:find_repo, params.merge(schema: 'cc'))
|
||||
end
|
||||
|
||||
# Get the public key for a given repository.
|
||||
#
|
||||
# This can be used to encrypt secure variables in the build configuration. See
|
||||
# [the encryption keys](http://about.travis-ci.org/docs/user/encryption-keys/) documentation page for more
|
||||
# information.
|
||||
#
|
||||
# ### Response
|
||||
#
|
||||
# json(:repository_key)
|
||||
get '/:owner_name/:name/key' do
|
||||
respond_with service(:find_repo_key, params), version: :v2
|
||||
end
|
||||
|
|
|
@ -4,11 +4,11 @@ class Travis::Api::App
|
|||
class Endpoint
|
||||
class Workers < Endpoint
|
||||
get '/' do
|
||||
respond_with service(:find_workers, params)
|
||||
respond_with service(:find_workers, params), type: :workers
|
||||
end
|
||||
|
||||
get '/:id' do
|
||||
respond_with service(:find_worker, params)
|
||||
respond_with service(:find_worker, params), type: :worker
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,6 +11,16 @@ class Travis::Api::App
|
|||
def public?
|
||||
scope == :public
|
||||
end
|
||||
|
||||
def required_params_match?
|
||||
return true unless token = env['travis.access_token']
|
||||
|
||||
if token.extra && (required_params = token.extra['required_params'])
|
||||
required_params.all? { |name, value| params[name] == value }
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.registered(app)
|
||||
|
@ -18,23 +28,35 @@ class Travis::Api::App
|
|||
app.helpers(Helpers)
|
||||
end
|
||||
|
||||
def scope(name)
|
||||
def scope(*names)
|
||||
condition do
|
||||
name = settings.default_scope if name == :default
|
||||
names = [settings.default_scope] if names == [:default]
|
||||
scopes = env['travis.access_token'].try(:scopes) || settings.anonymous_scopes
|
||||
headers['X-OAuth-Scopes'] = scopes.map(&:to_s).join(',')
|
||||
headers['X-Accepted-OAuth-Scopes'] = name.to_s
|
||||
|
||||
if scopes.include? name
|
||||
env['travis.scope'] = name
|
||||
headers['Vary'] = 'Accept'
|
||||
headers['Vary'] << ', Authorization' unless public?
|
||||
true
|
||||
elsif env['travis.access_token']
|
||||
pass { halt 403, "insufficient access" }
|
||||
else
|
||||
pass { halt 401, "no access token supplied" }
|
||||
result = names.any? do |name|
|
||||
if scopes.include?(name) && required_params_match?
|
||||
headers['X-OAuth-Scopes'] = scopes.map(&:to_s).join(',')
|
||||
headers['X-Accepted-OAuth-Scopes'] = name.to_s
|
||||
|
||||
env['travis.scope'] = name
|
||||
headers['Vary'] = 'Accept'
|
||||
headers['Vary'] << ', Authorization' unless public?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
if !result
|
||||
headers['X-OAuth-Scopes'] = scopes.map(&:to_s).join(',')
|
||||
headers['X-Accepted-OAuth-Scopes'] = names.first.to_s
|
||||
|
||||
if env['travis.access_token']
|
||||
pass { halt 403, "insufficient access" }
|
||||
else
|
||||
pass { halt 401, "no access token supplied" }
|
||||
end
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -7,6 +7,76 @@ class Travis::Api::App
|
|||
DEFAULT_VERSION = 'v1'
|
||||
DEFAULT_FORMAT = 'json'
|
||||
|
||||
class Entry
|
||||
SEPARATORS = Regexp.escape("()<>@,;:\/[]?={}\t ")
|
||||
TOKEN = /[^#{SEPARATORS}]+/
|
||||
attr_reader :type, :subtype, :quality, :version, :params
|
||||
def initialize(accept_string)
|
||||
@type, @subtype, @quality, @version, @params = parse(accept_string)
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
[1 - quality, mime_type.count('*'), 1 - params.size] <=>
|
||||
[1 - other.quality, other.mime_type.count('*'), 1 - other.params.size]
|
||||
end
|
||||
|
||||
def mime_type
|
||||
"#{type}/#{subtype}"
|
||||
end
|
||||
|
||||
def version
|
||||
version = @version || params['version']
|
||||
version ? "v#{version}" : nil
|
||||
end
|
||||
|
||||
def accepts?(mime_type)
|
||||
return true if self.mime_type == '*/*'
|
||||
|
||||
type, subtype = mime_type.scan(%r{(#{TOKEN})/(#{TOKEN})}).flatten
|
||||
type == self.type && (self.subtype == '*' || subtype == self.subtype)
|
||||
end
|
||||
|
||||
def to_s
|
||||
str = "#{mime_type}; q=#{quality}"
|
||||
str << "; #{params.map { |k,v| "#{k}=#{v}" }.join('; ')}" if params.length > 0
|
||||
str
|
||||
end
|
||||
|
||||
private
|
||||
def parse(str)
|
||||
# this handles only subset of what Accept header can
|
||||
# contain, only the simplest cases, no quoted strings etc.
|
||||
type, subtype, params = str.scan(%r{(#{TOKEN})/(#{TOKEN})(.*)}).flatten
|
||||
quality = 1
|
||||
|
||||
version = nil
|
||||
if params
|
||||
params = Hash[*params.split(';').map { |p| p.scan /(#{TOKEN})=(#{TOKEN})/ }.flatten]
|
||||
quality = params.delete('q').to_f if params['q']
|
||||
end
|
||||
|
||||
if subtype =~ HEADER_FORMAT
|
||||
subtype = $2
|
||||
version = $1
|
||||
end
|
||||
|
||||
[type, subtype, quality, version, params]
|
||||
end
|
||||
end
|
||||
|
||||
def accept_entries
|
||||
entries = env['HTTP_ACCEPT'].to_s.delete(' ').to_s.split(',').map { |e| Entry.new(e) }
|
||||
entries.empty? ? [Entry.new('*/*')] : entries.sort
|
||||
end
|
||||
|
||||
def acceptable_formats
|
||||
if format = env['travis.format_from_path']
|
||||
[Entry.new(Rack::Mime.mime_type(".#{format}"))]
|
||||
else
|
||||
accept_entries
|
||||
end
|
||||
end
|
||||
|
||||
def accept_version
|
||||
@accept_version ||= request.accept.join =~ HEADER_FORMAT && "v#{$1}" || DEFAULT_VERSION
|
||||
end
|
||||
|
|
17
lib/travis/api/app/helpers/db_follower.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
require 'travis/api/app'
|
||||
|
||||
class Travis::Api::App
|
||||
module Helpers
|
||||
module DbFollower
|
||||
def prefer_follower
|
||||
if Travis.config.use_database_follower?
|
||||
Octopus.using(:follower) do
|
||||
yield
|
||||
end
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -6,11 +6,12 @@ class Travis::Api::App
|
|||
# convert (in addition to the return values supported by Sinatra, of
|
||||
# course). These values will be encoded in JSON.
|
||||
module RespondWith
|
||||
include Accept
|
||||
|
||||
def respond_with(resource, options = {})
|
||||
options[:format] ||= env['travis.format']
|
||||
result = respond(resource, options)
|
||||
result = result ? result.to_json : 404
|
||||
halt result
|
||||
result = result.to_json if result && response.content_type =~ /application\/json/
|
||||
halt result || 404
|
||||
end
|
||||
|
||||
def body(value = nil, options = {}, &block)
|
||||
|
@ -21,15 +22,34 @@ class Travis::Api::App
|
|||
private
|
||||
|
||||
def respond(resource, options)
|
||||
responders(resource, options).each do |const|
|
||||
responder = const.new(self, resource, options)
|
||||
resource = responder.apply if responder.apply?
|
||||
resource = apply_service_responder(resource, options)
|
||||
|
||||
response = nil
|
||||
acceptable_formats.find do |accept|
|
||||
responders(resource, options).find do |const|
|
||||
responder = const.new(self, resource, options.dup.merge(accept: accept))
|
||||
response = responder.apply if responder.apply?
|
||||
end
|
||||
end
|
||||
|
||||
if responders = options[:responders]
|
||||
responders.each do |klass|
|
||||
responder = klass.new(self, response, options)
|
||||
response = responder.apply if responder.apply?
|
||||
end
|
||||
end
|
||||
|
||||
response || (resource ? error(406) : error(404))
|
||||
end
|
||||
|
||||
def apply_service_responder(resource, options)
|
||||
responder = Responders::Service.new(self, resource, options)
|
||||
resource = responder.apply if responder.apply?
|
||||
resource
|
||||
end
|
||||
|
||||
def responders(resource, options)
|
||||
[:Service, :Json, :Image, :Xml, :Plain].map do |name|
|
||||
[:Json, :Image, :Xml, :Plain].map do |name|
|
||||
Responders.const_get(name)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,6 +24,7 @@ class Travis::Api::App
|
|||
|
||||
def extract_format
|
||||
env['PATH_INFO'].sub!(FORMAT, '')
|
||||
env['travis.format_from_path'] = $1
|
||||
env['travis.format'] = $1 || accept_format
|
||||
end
|
||||
|
||||
|
@ -40,6 +41,7 @@ class Travis::Api::App
|
|||
end
|
||||
|
||||
def force_redirect(path)
|
||||
path += "?#{request.query_string}" unless request.query_string.empty?
|
||||
response.body = ''
|
||||
response['Content-Length'] = '0'
|
||||
response['Content-Type'] = ''
|
||||
|
|
|
@ -31,5 +31,23 @@ module Travis::Api::App::Responders
|
|||
def headers
|
||||
endpoint.headers
|
||||
end
|
||||
|
||||
def apply
|
||||
endpoint.content_type content_type
|
||||
end
|
||||
|
||||
def apply?
|
||||
resource && acceptable_format?
|
||||
end
|
||||
|
||||
def format
|
||||
self.class.name.split('::').last.downcase
|
||||
end
|
||||
|
||||
def acceptable_format?
|
||||
if accept = options[:accept]
|
||||
accept.accepts?(Rack::Mime.mime_type(".#{format}"))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,18 +1,22 @@
|
|||
module Travis::Api::App::Responders
|
||||
class Image < Base
|
||||
def apply?
|
||||
options[:format] == 'png'
|
||||
def format
|
||||
'png'
|
||||
end
|
||||
|
||||
def apply
|
||||
headers['Pragma'] = "no-cache"
|
||||
headers['Expires'] = Time.now.utc.httpdate
|
||||
headers['Content-Disposition'] = %(inline; filename="#{File.basename(filename)}")
|
||||
halt send_file(filename, type: :png)
|
||||
send_file(filename, type: :png, last_modified: last_modified)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def content_type
|
||||
'image/png'
|
||||
end
|
||||
|
||||
def filename
|
||||
"#{root}/public/images/result/#{result}.png"
|
||||
end
|
||||
|
@ -24,5 +28,10 @@ module Travis::Api::App::Responders
|
|||
def root
|
||||
File.expand_path('.') # TODO wat.
|
||||
end
|
||||
|
||||
def last_modified
|
||||
resource ? resource.last_build_finished_at : nil
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,21 +4,54 @@ class Travis::Api::App
|
|||
include Helpers::Accept
|
||||
|
||||
def apply?
|
||||
options[:format] == 'json' && !resource.is_a?(String) && !resource.nil?
|
||||
super && !resource.is_a?(String) && !resource.nil? && accepts_log?
|
||||
end
|
||||
|
||||
def apply
|
||||
halt result.to_json
|
||||
super
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def content_type
|
||||
'application/json;charset=utf-8'
|
||||
end
|
||||
|
||||
def accepts_log?
|
||||
return true unless resource.is_a?(Log)
|
||||
|
||||
chunked = accept_params[:chunked]
|
||||
chunked ? !resource.aggregated_at : true
|
||||
end
|
||||
|
||||
def result
|
||||
builder ? builder.new(resource, request.params).data : resource
|
||||
builder ? builder.new(resource, params).data : basic_type_resource
|
||||
end
|
||||
|
||||
def builder
|
||||
@builder ||= Travis::Api.builder(resource, { :version => accept_version }.merge(options))
|
||||
if defined?(@builder)
|
||||
@builder
|
||||
else
|
||||
@builder = Travis::Api.builder(resource, { :version => version }.merge(options))
|
||||
end
|
||||
end
|
||||
|
||||
def accept_params
|
||||
(options[:accept].params || {}).symbolize_keys
|
||||
end
|
||||
|
||||
def version
|
||||
options[:accept].version || Travis::Api::App::Helpers::Accept::DEFAULT_VERSION
|
||||
end
|
||||
|
||||
def params
|
||||
(request.params || {}).merge(accept_params)
|
||||
end
|
||||
|
||||
def basic_type_resource
|
||||
resource if resource.is_a?(Hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,26 +1,35 @@
|
|||
module Travis::Api::App::Responders
|
||||
class Plain < Base
|
||||
def format
|
||||
'txt'
|
||||
end
|
||||
|
||||
def apply?
|
||||
# make sure that we don't leak anything by processing only Artifact::Log
|
||||
# make sure that we don't leak anything by processing only Log
|
||||
# instances here. I don't want to create entire new API builder just
|
||||
# for log's content for now.
|
||||
#
|
||||
# TODO: think how to handle other formats correctly
|
||||
options[:format] == 'txt' && resource.is_a?(Artifact::Log)
|
||||
super && resource.is_a?(Log)
|
||||
end
|
||||
|
||||
def apply
|
||||
super
|
||||
|
||||
filename = resource.id
|
||||
disposition = params[:attachment] ? 'attachment' : 'inline'
|
||||
|
||||
headers['Content-Disposition'] = %(#{disposition}; filename="#{filename}")
|
||||
|
||||
endpoint.content_type 'text/plain'
|
||||
halt(params[:deansi] ? clear_ansi(resource.content) : resource.content)
|
||||
params[:deansi] ? clear_ansi(resource.content) : resource.content
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def content_type
|
||||
'text/plain'
|
||||
end
|
||||
|
||||
def clear_ansi(content)
|
||||
content.gsub(/\r\r/, "\r")
|
||||
.gsub(/^.*\r(?!$)/, '')
|
||||
|
|
|
@ -16,19 +16,25 @@ module Travis::Api::App::Responders
|
|||
}
|
||||
|
||||
def apply?
|
||||
options[:format] == 'xml'
|
||||
super && resource.is_a?(Repository)
|
||||
end
|
||||
|
||||
def apply
|
||||
halt TEMPLATE % data
|
||||
super
|
||||
|
||||
TEMPLATE % data
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def content_type
|
||||
'application/xml;charset=utf-8'
|
||||
end
|
||||
|
||||
def data
|
||||
{
|
||||
name: resource.slug,
|
||||
url: [Travis.config.domain, resource.slug].join('/'),
|
||||
url: File.join("https://", Travis.config.client_domain, resource.slug),
|
||||
activity: activity,
|
||||
label: last_build.try(:number),
|
||||
status: status,
|
||||
|
|
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 231 B |
BIN
public/images/result/error.png
Normal file
After Width: | Height: | Size: 932 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.4 KiB |
BIN
public/images/result/pending.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.1 KiB |
|
@ -3,6 +3,6 @@ cd "$(dirname "$0")/.."
|
|||
[ $PORT ] || PORT=3000
|
||||
[ $RACK_ENV ] || RACK_ENV=development
|
||||
|
||||
cmd="ruby -I lib -S bundle exec ruby -I lib -S unicorn config.ru -c config/unicorn.rb -p $PORT -E $RACK_ENV"
|
||||
[[ $RACK_ENV == "development" ]] && exec rerun "$cmd -l 127.0.0.1:$PORT"
|
||||
cmd="ruby -I lib -S bundle exec ruby -I lib -S puma config.ru -p $PORT -e $RACK_ENV --threads 0:16"
|
||||
[[ $RACK_ENV == "development" ]] && exec rerun "$cmd -b tcp://127.0.0.1:$PORT"
|
||||
exec $cmd
|
||||
|
|
32
spec/integration/formats_handling_spec.rb
Normal file
|
@ -0,0 +1,32 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'App' do
|
||||
before do
|
||||
FactoryGirl.create(:test, :number => '3.1', :queue => 'builds.common')
|
||||
|
||||
add_endpoint '/foo' do
|
||||
get '/' do
|
||||
respond_with(Log.first)
|
||||
end
|
||||
|
||||
get '/hash' do
|
||||
respond_with foo: 'bar'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'gives priority to format given the url' do
|
||||
response = get '/foo.txt', {}, 'HTTP_ACCEPT' => 'application/json'
|
||||
response.content_type.should =~ /^text\/plain/
|
||||
end
|
||||
|
||||
it 'responds with first available type' do
|
||||
response = get '/foo', {}, 'HTTP_ACCEPT' => 'image/jpeg, application/json'
|
||||
response.content_type.should =~ /^application\/json/
|
||||
end
|
||||
|
||||
it 'responds with 406 if server can\'t use any mime type' do
|
||||
response = get '/foo/hash', {}, 'HTTP_ACCEPT' => 'text/plain, image/jpeg'
|
||||
response.status.should == 406
|
||||
end
|
||||
end
|
30
spec/integration/responders_spec.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'App' do
|
||||
before do
|
||||
FactoryGirl.create(:test, :number => '3.1', :queue => 'builds.common')
|
||||
|
||||
responder = Class.new(Travis::Api::App::Responders::Base) do
|
||||
def apply?
|
||||
true
|
||||
end
|
||||
|
||||
def apply
|
||||
resource[:extra] = 'moar!'
|
||||
|
||||
resource
|
||||
end
|
||||
end
|
||||
|
||||
add_endpoint '/foo' do
|
||||
get '/hash' do
|
||||
respond_with({ foo: 'bar' }, responders: [responder])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'runs responder when rendering the response with respond_with' do
|
||||
response = get '/foo/hash', {}, 'HTTP_ACCEPT' => 'application/json'
|
||||
JSON.parse(response.body).should == { 'foo' => 'bar', 'extra' => 'moar!' }
|
||||
end
|
||||
end
|
49
spec/integration/scopes_spec.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'App' do
|
||||
before do
|
||||
FactoryGirl.create(:test, :number => '3.1', :queue => 'builds.common')
|
||||
|
||||
add_endpoint '/foo' do
|
||||
get '/:id/bar', scope: [:foo, :bar] do
|
||||
respond_with foo: 'bar'
|
||||
end
|
||||
|
||||
get '/:job_id/log' do
|
||||
respond_with job_id: params[:job_id]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'checks if token has one of the required scopes' do
|
||||
token = Travis::Api::App::AccessToken.new(app_id: 1, user_id: 2, scopes: [:foo]).tap(&:save)
|
||||
|
||||
response = get '/foo/1/bar', {}, 'HTTP_ACCEPT' => 'application/json; version=2', 'HTTP_AUTHORIZATION' => "token #{token.token}"
|
||||
response.should be_successful
|
||||
response.headers['X-Accepted-OAuth-Scopes'].should == 'foo'
|
||||
|
||||
token = Travis::Api::App::AccessToken.new(app_id: 1, user_id: 2, scopes: [:bar]).tap(&:save)
|
||||
|
||||
response = get '/foo/1/bar', {}, 'HTTP_ACCEPT' => 'application/json; version=2', 'HTTP_AUTHORIZATION' => "token #{token.token}"
|
||||
response.should be_successful
|
||||
response.headers['X-Accepted-OAuth-Scopes'].should == 'bar'
|
||||
|
||||
token = Travis::Api::App::AccessToken.new(app_id: 1, user_id: 2, scopes: [:baz]).tap(&:save)
|
||||
|
||||
response = get '/foo/1/bar', {}, 'HTTP_ACCEPT' => 'application/json; version=2', 'HTTP_AUTHORIZATION' => "token #{token.token}"
|
||||
response.status.should == 404
|
||||
end
|
||||
|
||||
it 'checks if required_params match the from the request' do
|
||||
extra = {
|
||||
required_params: { job_id: '10' }
|
||||
}
|
||||
token = Travis::Api::App::AccessToken.new(app_id: 1, user_id: 2, extra: extra).tap(&:save)
|
||||
|
||||
response = get '/foo/10/log', {}, 'HTTP_ACCEPT' => 'application/json', 'HTTP_AUTHORIZATION' => "token #{token.token}"
|
||||
response.should be_successful
|
||||
|
||||
response = get '/foo/11/log', {}, 'HTTP_ACCEPT' => 'application/json', 'HTTP_AUTHORIZATION' => "token #{token.token}"
|
||||
response.status.should == 403
|
||||
end
|
||||
end
|
|
@ -1,8 +1,6 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'Branches' do
|
||||
before { Scenario.default }
|
||||
|
||||
let(:repo) { Repository.by_slug('svenfuchs/minimal').first }
|
||||
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.1+json' } }
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'Builds' do
|
||||
before { Scenario.default }
|
||||
|
||||
let(:repo) { Repository.by_slug('svenfuchs/minimal').first }
|
||||
let(:build) { repo.builds.first }
|
||||
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.1+json' } }
|
||||
|
|
|
@ -2,7 +2,6 @@ require 'spec_helper'
|
|||
|
||||
describe 'Hooks' do
|
||||
before(:each) do
|
||||
Scenario.default
|
||||
user.permissions.create repository: repo, admin: true
|
||||
end
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'v1 repos' do
|
||||
before(:each) { Scenario.default }
|
||||
|
||||
let(:repo) { Repository.by_slug('svenfuchs/minimal').first }
|
||||
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.1+json' } }
|
||||
|
||||
|
@ -42,10 +40,6 @@ describe 'v1 repos' do
|
|||
end
|
||||
|
||||
describe 'GET /svenfuchs/minimal.png' do
|
||||
it '"unknown" when the repository does not exist' do
|
||||
get('/svenfuchs/does-not-exist.png').should deliver_result_image_for('unknown')
|
||||
end
|
||||
|
||||
it '"unknown" when it only has one build that is not finished' do
|
||||
Build.delete_all
|
||||
Factory(:build, repository: repo, state: :created, result: nil)
|
||||
|
@ -74,10 +68,6 @@ describe 'v1 repos' do
|
|||
let(:on_foo) { Factory(:commit, branch: 'foo') }
|
||||
let(:on_bar) { Factory(:commit, branch: 'bar') }
|
||||
|
||||
it '"unknown" when the repository does not exist' do
|
||||
get('/svenfuchs/does-not-exist.png?branch=foo,bar').should deliver_result_image_for('unknown')
|
||||
end
|
||||
|
||||
it '"unknown" when it only has unfinished builds on the relevant branches' do
|
||||
Build.delete_all
|
||||
Factory(:build, repository: repo, state: :started, commit: on_foo)
|
||||
|
|
|
@ -1,21 +1,12 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'Workers' do
|
||||
before(:each) do
|
||||
Time.stubs(:now).returns(Time.utc(2011, 11, 11, 11, 11, 11))
|
||||
@workers = [
|
||||
Factory(:worker, :name => 'worker-1', :state => :working),
|
||||
Factory(:worker, :name => 'worker-2', :state => :errored)
|
||||
]
|
||||
end
|
||||
|
||||
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.1+json' } }
|
||||
|
||||
attr_reader :workers
|
||||
let!(:workers) { [Worker.create(full_name: 'one'), Worker.create(full_name: 'two')] }
|
||||
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.1+json' } }
|
||||
|
||||
it 'GET /workers' do
|
||||
response = get '/workers', {}, headers
|
||||
response.should deliver_json_for(Worker.order(:host, :name), version: 'v1')
|
||||
response.should deliver_json_for(Worker.all, version: 'v1', type: 'workers')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'Branches' do
|
||||
before { Scenario.default }
|
||||
|
||||
let(:repo) { Repository.by_slug('svenfuchs/minimal').first }
|
||||
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.2+json' } }
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'Builds' do
|
||||
before { Scenario.default }
|
||||
|
||||
let(:repo) { Repository.by_slug('svenfuchs/minimal').first }
|
||||
let(:build) { repo.builds.first }
|
||||
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.2+json' } }
|
||||
|
@ -31,4 +29,9 @@ describe 'Builds' do
|
|||
response = get "/repos/svenfuchs/minimal/builds/#{build.id}", {}, headers
|
||||
response.should deliver_json_for(build, version: 'v2')
|
||||
end
|
||||
|
||||
it 'GET /builds/1?repository_id=1&branches=true' do
|
||||
response = get "/builds?repository_id=#{repo.id}&branches=true", {}, headers
|
||||
response.should deliver_json_for(repo.last_finished_builds_by_branches, version: 'v2')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,6 @@ require 'travis/testing/payloads'
|
|||
|
||||
describe 'Hooks' do
|
||||
before(:each) do
|
||||
Scenario.default
|
||||
user.permissions.create repository: repo, admin: true
|
||||
end
|
||||
|
||||
|
|
|
@ -13,8 +13,66 @@ describe 'Jobs' do
|
|||
response.should deliver_json_for(Job.queued('builds.common'), version: 'v2')
|
||||
end
|
||||
|
||||
it '/jobs/:job_id' do
|
||||
it '/jobs/:id' do
|
||||
response = get "/jobs/#{job.id}", {}, headers
|
||||
response.should deliver_json_for(job, version: 'v2')
|
||||
end
|
||||
|
||||
context 'GET /jobs/:job_id/log.txt' do
|
||||
it 'returns log for a job' do
|
||||
job.log.update_attributes!(content: 'the log')
|
||||
response = get "/jobs/#{job.id}/log.txt", {}, headers
|
||||
response.should deliver_as_txt('the log', version: 'v2')
|
||||
end
|
||||
|
||||
context 'when log is archived' do
|
||||
it 'redirects to archive' do
|
||||
job.log.update_attributes!(content: 'the log', archived_at: Time.now, archive_verified: true)
|
||||
response = get "/jobs/#{job.id}/log.txt", {}, headers
|
||||
response.should redirect_to("https://s3.amazonaws.com/archive.travis-ci.org/jobs/#{job.id}/log.txt")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when log is missing' do
|
||||
it 'redirects to archive' do
|
||||
job.log.destroy
|
||||
response = get "/jobs/#{job.id}/log.txt", {}, headers
|
||||
response.should redirect_to("https://s3.amazonaws.com/archive.travis-ci.org/jobs/#{job.id}/log.txt")
|
||||
end
|
||||
end
|
||||
|
||||
context 'with cors_hax param' do
|
||||
it 'renders No Content response with location of the archived log' do
|
||||
job.log.destroy
|
||||
response = get "/jobs/#{job.id}/log.txt?cors_hax=true", {}, headers
|
||||
response.status.should == 204
|
||||
response.headers['Location'].should == "https://s3.amazonaws.com/archive.travis-ci.org/jobs/#{job.id}/log.txt"
|
||||
end
|
||||
end
|
||||
|
||||
context 'with chunked log requested' do
|
||||
it 'responds with 406 when log is already aggregated' do
|
||||
job.log.update_attributes(aggregated_at: Time.now)
|
||||
headers = { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.2+json; chunked=true' }
|
||||
response = get "/jobs/#{job.id}/log", {}, headers
|
||||
response.status.should == 406
|
||||
end
|
||||
|
||||
it 'responds with chunks instead of full log' do
|
||||
job.log.parts << Log::Part.new(content: 'foo', number: 1, final: false)
|
||||
job.log.parts << Log::Part.new(content: 'bar', number: 2, final: true)
|
||||
|
||||
headers = { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.2+json; chunked=true' }
|
||||
response = get "/jobs/#{job.id}/log", {}, headers
|
||||
response.should deliver_json_for(job.log, version: 'v2', params: { chunked: true})
|
||||
end
|
||||
|
||||
it 'responds with full log if chunks are not available and full log is accepted' do
|
||||
job.log.update_attributes(aggregated_at: Time.now)
|
||||
headers = { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.2+json; chunked=true, application/vnd.travis-ci.2+json' }
|
||||
response = get "/jobs/#{job.id}/log", {}, headers
|
||||
response.should deliver_json_for(job.log, version: 'v2')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'Repos' do
|
||||
before(:each) { Scenario.default }
|
||||
|
||||
let(:repo) { Repository.by_slug('svenfuchs/minimal').first }
|
||||
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.2+json' } }
|
||||
|
||||
|
@ -76,6 +74,7 @@ describe 'Repos' do
|
|||
it 'GET /repos/1/cc.xml' do
|
||||
response = get "repos/#{repo.id}/cc.xml"
|
||||
response.should deliver_cc_xml_for(Repository.by_slug('svenfuchs/minimal').first)
|
||||
response.content_type.should eq('application/xml;charset=utf-8')
|
||||
end
|
||||
|
||||
it 'GET /repos/svenfuchs/minimal' do
|
||||
|
@ -88,15 +87,26 @@ describe 'Repos' do
|
|||
response.should deliver_cc_xml_for(Repository.by_slug('svenfuchs/minimal').first)
|
||||
end
|
||||
|
||||
it 'does not respond with cc.xml for /repos list' do
|
||||
response = get '/repos', {}, 'HTTP_ACCEPT' => 'application/xml; version=2'
|
||||
response.status.should == 406
|
||||
end
|
||||
|
||||
it 'responds with 404 when repo can\'t be found and format is png' do
|
||||
result = get('/repos/foo/bar.png', {}, 'HTTP_ACCEPT' => 'image/png; version=2')
|
||||
result.status.should == 404
|
||||
end
|
||||
|
||||
it 'responds with 404 when repo can\'t be found and format is other than png' do
|
||||
result = get('/repos/foo/bar', {}, 'HTTP_ACCEPT' => 'application/json; version=2')
|
||||
result.status.should == 404
|
||||
JSON.parse(result.body).should == { 'file' => 'not found' }
|
||||
end
|
||||
|
||||
describe 'GET /repos/svenfuchs/minimal.png?branch=foo,bar' do
|
||||
let(:on_foo) { Factory(:commit, branch: 'foo') }
|
||||
let(:on_bar) { Factory(:commit, branch: 'bar') }
|
||||
|
||||
it '"unknown" when the repository does not exist' do
|
||||
result = get('/repos/svenfuchs/does-not-exist.png?branch=foo,bar', {}, headers)
|
||||
result.should deliver_result_image_for('unknown')
|
||||
end
|
||||
|
||||
it '"unknown" when it only has unfinished builds on the relevant branches' do
|
||||
Build.delete_all
|
||||
Factory(:build, repository: repo, state: :started, commit: on_foo)
|
||||
|
@ -117,6 +127,7 @@ describe 'Repos' do
|
|||
Factory(:build, repository: repo, state: :passed, commit: on_bar)
|
||||
result = get('/repos/svenfuchs/minimal.png?branch=foo,bar', {}, headers)
|
||||
result.should deliver_result_image_for('passing')
|
||||
result.headers['Last-Modified'].should == repo.last_build_finished_at.httpdate
|
||||
end
|
||||
|
||||
it '"passing" when there is a running build but the previous one has passed' do
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'Users' do
|
||||
let(:user) { Factory(:user, locale: 'en') }
|
||||
let(:user) { User.first }
|
||||
let(:token) { Travis::Api::App::AccessToken.create(user: user, app_id: -1) }
|
||||
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.2+json', 'HTTP_AUTHORIZATION' => "token #{token}" } }
|
||||
|
||||
|
@ -17,6 +17,7 @@ describe 'Users' do
|
|||
|
||||
context 'POST /users/sync' do
|
||||
it 'syncs current_user repos' do
|
||||
user.update_attribute :is_syncing, false
|
||||
response = post "/users/sync", {}, headers
|
||||
response.should be_successful
|
||||
end
|
||||
|
|
|
@ -1,21 +1,12 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'Workers' do
|
||||
before(:each) do
|
||||
Time.stubs(:now).returns(Time.utc(2011, 11, 11, 11, 11, 11))
|
||||
@workers = [
|
||||
Factory(:worker, :name => 'worker-1', :state => :working),
|
||||
Factory(:worker, :name => 'worker-2', :state => :errored)
|
||||
]
|
||||
end
|
||||
|
||||
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.2+json' } }
|
||||
|
||||
attr_reader :workers
|
||||
let!(:workers) { [Worker.create(full_name: 'one'), Worker.create(full_name: 'two')] }
|
||||
let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.2+json' } }
|
||||
|
||||
it 'GET /workers' do
|
||||
response = get '/workers', {}, headers
|
||||
response.should deliver_json_for(Worker.order(:host, :name), version: 'v2')
|
||||
response.should deliver_json_for(Worker.all, version: 'v2', type: 'workers')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
26
spec/integration/version_spec.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'App' do
|
||||
before do
|
||||
add_endpoint '/foo' do
|
||||
get '/' do
|
||||
respond_with foo: 'bar'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'uses version from current accept header' do
|
||||
Travis::Api.expects(:builder).with { |r, options| options[:version] == 'v1' }
|
||||
|
||||
Travis::Api::App::Responders::Json.any_instance.stubs(:apply?).
|
||||
returns(false).then.returns(true)
|
||||
|
||||
response = get '/foo', {}, 'HTTP_ACCEPT' => 'application/json; version=2, application/json; version=1'
|
||||
response.content_type.should == 'application/json;charset=utf-8'
|
||||
end
|
||||
|
||||
it 'uses v1 by default' do
|
||||
Travis::Api.expects(:builder).with { |r, options| options[:version] == 'v1' }
|
||||
get '/foo', {}, 'HTTP_ACCEPT' => 'application/json'
|
||||
end
|
||||
end
|
|
@ -14,6 +14,7 @@ require 'support/matchers'
|
|||
|
||||
Travis.logger = Logger.new(StringIO.new)
|
||||
Travis::Api::App.setup
|
||||
Travis.config.client_domain = "www.example.com"
|
||||
|
||||
module TestHelpers
|
||||
include Sinatra::TestHelpers
|
||||
|
@ -41,7 +42,8 @@ RSpec.configure do |c|
|
|||
|
||||
c.before :suite do
|
||||
DatabaseCleaner.strategy = :transaction
|
||||
DatabaseCleaner.clean_with :transaction
|
||||
DatabaseCleaner.clean_with :truncation
|
||||
Scenario.default
|
||||
end
|
||||
|
||||
c.before :each do
|
||||
|
|
|
@ -24,6 +24,26 @@ RSpec::Matchers.define :deliver_json_for do |resource, options = {}|
|
|||
end
|
||||
end
|
||||
|
||||
RSpec::Matchers.define :deliver_as_txt do |expected, options = {}|
|
||||
match do |response|
|
||||
if response.status == 200
|
||||
failure_message_for_should do
|
||||
"expected\n\n#{actual}\n\nto equal\n\n#{expected}"
|
||||
end
|
||||
response.body.to_s == expected
|
||||
else
|
||||
failure_message_for_should do
|
||||
"expected the request to be successful (200) but was #{response.status}"
|
||||
end
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def parse(body)
|
||||
MultiJson.decode(body)
|
||||
end
|
||||
end
|
||||
|
||||
RSpec::Matchers.define :deliver_result_image_for do |name|
|
||||
match do |response|
|
||||
header = response.headers['content-disposition']
|
||||
|
@ -42,7 +62,7 @@ RSpec::Matchers.define :deliver_cc_xml_for do |repo|
|
|||
"expected #{body} to be a valid cc.xml"
|
||||
end
|
||||
|
||||
body.include?('<Projects>') && body.include?(%(name="#{repo.slug}"))
|
||||
body.include?('<Projects>') && body.include?(%(name="#{repo.slug}")) && body.include?("https://www.example.com/#{repo.slug}")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
49
spec/unit/access_token_spec.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::Api::App::AccessToken do
|
||||
it 'errors out on wrong type of :expires_in argument' do
|
||||
expect {
|
||||
described_class.new(app_id: 1, user_id: 2, expires_in: 'foo')
|
||||
}.to raise_error(ArgumentError, 'expires_in must be of integer type')
|
||||
end
|
||||
|
||||
it 'allows to skip expires_in' do
|
||||
expect {
|
||||
described_class.new(app_id: 1, user_id: 2, expires_in: nil)
|
||||
}.to_not raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it 'does not reuse token if expires_in is set' do
|
||||
token = described_class.new(app_id: 1, user_id: 2).tap(&:save)
|
||||
new_token = described_class.new(app_id: 1, user_id: 2, expires_in: 10)
|
||||
|
||||
token.token.should_not == new_token.token
|
||||
end
|
||||
|
||||
it 'expires the token after given period of time' do
|
||||
token = described_class.new(app_id: 1, user_id: 2, expires_in: 1).tap(&:save)
|
||||
|
||||
described_class.find_by_token(token.token).should_not be_nil
|
||||
|
||||
sleep 2
|
||||
|
||||
described_class.find_by_token(token.token).should be_nil
|
||||
end
|
||||
|
||||
it 'allows to save extra information' do
|
||||
attrs = {
|
||||
app_id: 1,
|
||||
user_id: 3,
|
||||
expires_in: 1,
|
||||
extra: {
|
||||
required_params: { job_id: '1' }
|
||||
}
|
||||
}
|
||||
|
||||
token = described_class.new(attrs).tap(&:save)
|
||||
token.extra.should == attrs[:extra]
|
||||
|
||||
token = described_class.find_by_token(token.token)
|
||||
token.extra.should == { 'required_params' => { 'job_id' => '1' } }
|
||||
end
|
||||
end
|
|
@ -18,7 +18,7 @@ describe Travis::Api::App::Endpoint::Accounts do
|
|||
'login' => user.login,
|
||||
'name' => user.name,
|
||||
'type' => 'user',
|
||||
'repos_count' => nil
|
||||
'repos_count' => 1
|
||||
}]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,10 +26,10 @@ describe Travis::Api::App::Endpoint::Authorization do
|
|||
describe 'POST /auth/github' do
|
||||
before do
|
||||
data = { 'id' => user.github_id, 'name' => user.name, 'login' => user.login, 'gravatar_id' => user.gravatar_id }
|
||||
GH.stubs(:with).with(token: 'private repos').returns stub(:[] => user.login, :headers => {'x-oauth-scopes' => 'repo'}, :to_hash => data)
|
||||
GH.stubs(:with).with(token: 'public repos').returns stub(:[] => user.login, :headers => {'x-oauth-scopes' => 'public_repo'}, :to_hash => data)
|
||||
GH.stubs(:with).with(token: 'no repos').returns stub(:[] => user.login, :headers => {'x-oauth-scopes' => 'user'}, :to_hash => data)
|
||||
GH.stubs(:with).with(token: 'invalid token').raises(Faraday::Error::ClientError, 'CLIENT ERROR!')
|
||||
GH.stubs(:with).with(token: 'private repos', client_id: nil).returns stub(:[] => user.login, :headers => {'x-oauth-scopes' => 'repo'}, :to_hash => data)
|
||||
GH.stubs(:with).with(token: 'public repos', client_id: nil).returns stub(:[] => user.login, :headers => {'x-oauth-scopes' => 'public_repo'}, :to_hash => data)
|
||||
GH.stubs(:with).with(token: 'no repos', client_id: nil).returns stub(:[] => user.login, :headers => {'x-oauth-scopes' => 'user'}, :to_hash => data)
|
||||
GH.stubs(:with).with(token: 'invalid token', client_id: nil).raises(Faraday::Error::ClientError, 'CLIENT ERROR!')
|
||||
end
|
||||
|
||||
def get_token(github_token)
|
||||
|
|
71
spec/unit/helpers/accept_spec.rb
Normal file
|
@ -0,0 +1,71 @@
|
|||
require 'spec_helper'
|
||||
|
||||
module Travis::Api::App::Helpers
|
||||
describe Accept do
|
||||
class FakeApp < Struct.new(:env)
|
||||
include Accept
|
||||
end
|
||||
|
||||
it 'returns accept entries sorted properly' do
|
||||
accept = "text/html; q=0.2; level=1, application/vnd.travis-ci.2+json, text/*, text/html;level=2; q=0.5"
|
||||
FakeApp.new('HTTP_ACCEPT' => accept).accept_entries.map(&:to_s).should ==
|
||||
["application/json; q=1", "text/*; q=1", "text/html; q=0.5; level=2", "text/html; q=0.2; level=1"]
|
||||
end
|
||||
|
||||
it 'properly parses params, quality and version' do
|
||||
accept = "application/vnd.travis-ci.2+json; q=0.2; level=1; foo=bar"
|
||||
accept_entry = FakeApp.new('HTTP_ACCEPT' => accept).accept_entries.first
|
||||
accept_entry.quality.should == 0.2
|
||||
accept_entry.params.should == { 'level' => '1', 'foo' => 'bar' }
|
||||
accept_entry.mime_type.should == 'application/json'
|
||||
accept_entry.version.should == 'v2'
|
||||
end
|
||||
|
||||
it 'returns */* for empty accept header' do
|
||||
accept_entry = FakeApp.new({}).accept_entries.first
|
||||
accept_entry.mime_type.should == '*/*'
|
||||
end
|
||||
|
||||
describe Accept::Entry do
|
||||
describe 'version' do
|
||||
it 'can be passed as a vendor extension' do
|
||||
entry = Accept::Entry.new('application/vnd.travis-ci.2+json')
|
||||
entry.version.should == 'v2'
|
||||
end
|
||||
|
||||
it 'can be passed as a param' do
|
||||
entry = Accept::Entry.new('application/json; version=2')
|
||||
entry.version.should == 'v2'
|
||||
end
|
||||
|
||||
it 'has a higher priority when in vendor extension' do
|
||||
entry = Accept::Entry.new('application/vnd.travis-ci.1+json; version=2')
|
||||
entry.version.should == 'v1'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'accepts?' do
|
||||
it 'accepts everything with */* type' do
|
||||
entry = Accept::Entry.new('*/*')
|
||||
entry.accepts?('application/json').should be_true
|
||||
entry.accepts?('foo/bar').should be_true
|
||||
end
|
||||
|
||||
it 'accepts every subtype with application/* type' do
|
||||
entry = Accept::Entry.new('application/*')
|
||||
|
||||
entry.accepts?('application/foo').should be_true
|
||||
entry.accepts?('application/bar').should be_true
|
||||
entry.accepts?('text/plain').should be_false
|
||||
end
|
||||
|
||||
it 'accepts when type and subtype match' do
|
||||
entry = Accept::Entry.new('application/json')
|
||||
|
||||
entry.accepts?('application/json').should be_true
|
||||
entry.accepts?('application/xml').should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
52
spec/unit/responders/json_spec.rb
Normal file
|
@ -0,0 +1,52 @@
|
|||
require 'spec_helper'
|
||||
|
||||
module Travis::Api::App::Responders
|
||||
describe Json do
|
||||
class MyJson < Json
|
||||
end
|
||||
|
||||
let(:request) { stub 'request', params: {} }
|
||||
let(:endpoint) { stub 'endpoint', request: request, content_type: nil }
|
||||
let(:resource) { stub 'resource' }
|
||||
let(:accept) { stub 'accept entry', version: '2', params: {} }
|
||||
let(:options) { { :accept => accept} }
|
||||
let(:json) { MyJson.new(endpoint, resource, options) }
|
||||
|
||||
context 'with resource not associated with Api data class' do
|
||||
it 'returns nil result' do
|
||||
json.apply.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context 'with resource being' do
|
||||
context 'a Hash instance' do
|
||||
let(:resource) { { foo: 'bar' } }
|
||||
|
||||
it 'returns resource converted to_json' do
|
||||
json.apply.should == { foo: 'bar' }
|
||||
end
|
||||
end
|
||||
|
||||
context 'nil' do
|
||||
let(:resource) { nil }
|
||||
|
||||
it 'responds with 404' do
|
||||
json.apply?.should be_false
|
||||
json.apply.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with resource associated with Api data class' do
|
||||
let(:builder) { stub 'builder', data: { foo: 'bar' } }
|
||||
let(:builder_class) { stub 'builder class', new: builder }
|
||||
before do
|
||||
json.stubs :builder => builder_class
|
||||
end
|
||||
|
||||
it 'returns proper data converted to json' do
|
||||
json.apply.should == { foo: 'bar' }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -12,9 +12,14 @@ Gem::Specification.new do |s|
|
|||
"Sven Fuchs",
|
||||
"Konstantin Haase",
|
||||
"Piotr Sarnacki",
|
||||
"Henrik Hodne",
|
||||
"Mathias Meyer",
|
||||
"Josh Kalderimis",
|
||||
"Andre Arko",
|
||||
"Erik Michaels-Ober",
|
||||
"Steve Richert",
|
||||
"Brian Ford",
|
||||
"Henrik Hodne"
|
||||
"Nick Schonning"
|
||||
]
|
||||
|
||||
s.email = [
|
||||
|
@ -22,12 +27,18 @@ Gem::Specification.new do |s|
|
|||
"konstantin.mailinglists@googlemail.com",
|
||||
"drogus@gmail.com",
|
||||
"meyer@paperplanes.de",
|
||||
"me@henrikhodne.com",
|
||||
"svenfuchs@artweb-design.de",
|
||||
"josh.kalderimis@gmail.com",
|
||||
"andre@arko.net",
|
||||
"sferik@gmail.com",
|
||||
"steve.richert@gmail.com",
|
||||
"bford@engineyard.com",
|
||||
"me@henrikhodne.com"
|
||||
"nschonni@gmail.com"
|
||||
]
|
||||
|
||||
s.files = [
|
||||
"CONTRIBUTING.md",
|
||||
"Procfile",
|
||||
"README.md",
|
||||
"Rakefile",
|
||||
|
@ -37,6 +48,7 @@ Gem::Specification.new do |s|
|
|||
"config/unicorn.rb",
|
||||
"docs/00_overview.md",
|
||||
"docs/01_cross_origin.md",
|
||||
"lib/tasks/build_update_pull_request_data.rake",
|
||||
"lib/travis/api/app.rb",
|
||||
"lib/travis/api/app/access_token.rb",
|
||||
"lib/travis/api/app/base.rb",
|
||||
|
@ -54,6 +66,7 @@ Gem::Specification.new do |s|
|
|||
"lib/travis/api/app/endpoint/documentation/css/bootstrap.css",
|
||||
"lib/travis/api/app/endpoint/documentation/css/bootstrap.min.css",
|
||||
"lib/travis/api/app/endpoint/documentation/css/prettify.css",
|
||||
"lib/travis/api/app/endpoint/documentation/css/style.css",
|
||||
"lib/travis/api/app/endpoint/documentation/img/glyphicons-halflings-white.png",
|
||||
"lib/travis/api/app/endpoint/documentation/img/glyphicons-halflings.png",
|
||||
"lib/travis/api/app/endpoint/documentation/img/grid-18px-masked.png",
|
||||
|
@ -79,11 +92,13 @@ Gem::Specification.new do |s|
|
|||
"lib/travis/api/app/endpoint/documentation/js/lang-xq.js",
|
||||
"lib/travis/api/app/endpoint/documentation/js/lang-yaml.js",
|
||||
"lib/travis/api/app/endpoint/documentation/js/prettify.js",
|
||||
"lib/travis/api/app/endpoint/documentation/resources.rb",
|
||||
"lib/travis/api/app/endpoint/endpoints.rb",
|
||||
"lib/travis/api/app/endpoint/events.rb",
|
||||
"lib/travis/api/app/endpoint/home.rb",
|
||||
"lib/travis/api/app/endpoint/hooks.rb",
|
||||
"lib/travis/api/app/endpoint/jobs.rb",
|
||||
"lib/travis/api/app/endpoint/logs.rb",
|
||||
"lib/travis/api/app/endpoint/repos.rb",
|
||||
"lib/travis/api/app/endpoint/requests.rb",
|
||||
"lib/travis/api/app/endpoint/stats.rb",
|
||||
|
@ -107,13 +122,18 @@ Gem::Specification.new do |s|
|
|||
"lib/travis/api/app/responders/base.rb",
|
||||
"lib/travis/api/app/responders/image.rb",
|
||||
"lib/travis/api/app/responders/json.rb",
|
||||
"lib/travis/api/app/responders/plain.rb",
|
||||
"lib/travis/api/app/responders/service.rb",
|
||||
"lib/travis/api/app/responders/xml.rb",
|
||||
"public/favicon.ico",
|
||||
"public/images/result/error.png",
|
||||
"public/images/result/failing.png",
|
||||
"public/images/result/passing.png",
|
||||
"public/images/result/pending.png",
|
||||
"public/images/result/unknown.png",
|
||||
"script/console",
|
||||
"script/server",
|
||||
"spec/integration/formats_handling_spec.rb",
|
||||
"spec/integration/routes.backup.rb",
|
||||
"spec/integration/v1/branches_spec.rb",
|
||||
"spec/integration/v1/builds_spec.rb",
|
||||
|
@ -130,6 +150,7 @@ Gem::Specification.new do |s|
|
|||
"spec/integration/v2/users_spec.rb",
|
||||
"spec/integration/v2/workers_spec.rb",
|
||||
"spec/integration/v2_spec.backup.rb",
|
||||
"spec/integration/version_spec.rb",
|
||||
"spec/spec_helper.rb",
|
||||
"spec/support/matchers.rb",
|
||||
"spec/unit/app_spec.rb",
|
||||
|
@ -152,10 +173,12 @@ Gem::Specification.new do |s|
|
|||
"spec/unit/extensions/scoping_spec.rb",
|
||||
"spec/unit/extensions/smart_constants_spec.rb",
|
||||
"spec/unit/extensions/subclass_tracker_spec.rb",
|
||||
"spec/unit/helpers/accept_spec.rb",
|
||||
"spec/unit/helpers/json_renderer_spec.rb",
|
||||
"spec/unit/middleware/logging_spec.rb",
|
||||
"spec/unit/middleware/scope_check_spec.rb",
|
||||
"spec/unit/middleware_spec.rb",
|
||||
"spec/unit/responders/json_spec.rb",
|
||||
"spec/unit/responders/service_spec.rb",
|
||||
"travis-api.gemspec"
|
||||
]
|
||||
|
@ -166,12 +189,12 @@ Gem::Specification.new do |s|
|
|||
s.add_dependency 'hubble', '~> 0.1'
|
||||
s.add_dependency 'backports', '~> 2.5'
|
||||
s.add_dependency 'pg', '~> 0.13.2'
|
||||
s.add_dependency 'newrelic_rpm', '~> 3.5.0'
|
||||
s.add_dependency 'newrelic_rpm', '~> 3.6.1.88'
|
||||
s.add_dependency 'thin', '~> 1.4'
|
||||
s.add_dependency 'sinatra', '~> 1.3'
|
||||
s.add_dependency 'sinatra-contrib', '~> 1.3'
|
||||
s.add_dependency 'redcarpet', '~> 2.1'
|
||||
s.add_dependency 'rack-ssl', '~> 1.3'
|
||||
s.add_dependency 'rack-ssl', '~> 1.3', '>= 1.3.3'
|
||||
s.add_dependency 'rack-contrib', '~> 1.1'
|
||||
end
|
||||
|
||||
|
|