bait 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4a26df53b8594473bd65a349db9746a2f58eae3b
4
+ data.tar.gz: 3b2c21ed9b86815ea4cbc542f9ab38548f02132a
5
+ SHA512:
6
+ metadata.gz: a2a12d75d3bd4b57a843ae680e99d4add6c736721889d087353c97402389339a5a3034f397702ddc83cc3d714c7e164bd4cdac35468c1e990c58dfdcb1829183
7
+ data.tar.gz: e8ac8c6fe4f7e52101224a059fed668923a77daf9acbea3f8349f74c32141788056b9fb6baaeb4cd8e79626509fc232dcfbec9acad7a59f6f95372f3a2a97c29
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ rmts_dir=$(dirname $0)
3
+ project_dir="$rmts_dir/.."
4
+ cd $project_dir
5
+
6
+ bundle
7
+ bundle exec rspec spec
@@ -0,0 +1,20 @@
1
+ .DS_Store
2
+ *.swp
3
+ *.gem
4
+ *.rbc
5
+ .bundle
6
+ .config
7
+ coverage
8
+ InstalledFiles
9
+ lib/bundler/man
10
+ pkg
11
+ rdoc
12
+ spec/reports
13
+ test/tmp
14
+ test/version_tmp
15
+ tmp
16
+
17
+ # YARD artifacts
18
+ .yardoc
19
+ _yardoc
20
+ doc/
data/DEVELOP ADDED
@@ -0,0 +1,5 @@
1
+ Write the specs
2
+
3
+ http://www.sinatrarb.com/testing.html
4
+ https://github.com/raggi/github_post_receive_server/blob/master/spec/github_post_receive_server/rack_app_spec.rb
5
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rmts.gemspec
4
+ gemspec
@@ -0,0 +1,106 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ bait (0.1.1)
5
+ git
6
+ haml
7
+ moneta
8
+ sinatra
9
+ thin
10
+ toystore
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ activemodel (3.2.14)
16
+ activesupport (= 3.2.14)
17
+ builder (~> 3.0.0)
18
+ activesupport (3.2.14)
19
+ i18n (~> 0.6, >= 0.6.4)
20
+ multi_json (~> 1.0)
21
+ adapter (0.7.0)
22
+ builder (3.0.4)
23
+ coderay (1.0.9)
24
+ daemons (1.1.9)
25
+ diff-lcs (1.2.4)
26
+ eventmachine (1.0.3)
27
+ ffi (1.9.0)
28
+ formatador (0.2.4)
29
+ git (1.2.5)
30
+ guard (1.8.1)
31
+ formatador (>= 0.2.4)
32
+ listen (>= 1.0.0)
33
+ lumberjack (>= 1.0.2)
34
+ pry (>= 0.9.10)
35
+ thor (>= 0.14.6)
36
+ guard-rspec (3.0.2)
37
+ guard (>= 1.8)
38
+ rspec (~> 2.13)
39
+ haml (4.0.2)
40
+ tilt
41
+ i18n (0.6.4)
42
+ listen (1.2.2)
43
+ rb-fsevent (>= 0.9.3)
44
+ rb-inotify (>= 0.9)
45
+ rb-kqueue (>= 0.2)
46
+ lumberjack (1.0.4)
47
+ method_source (0.8.1)
48
+ moneta (0.7.19)
49
+ multi_json (1.7.7)
50
+ pry (0.9.12.2)
51
+ coderay (~> 1.0.5)
52
+ method_source (~> 0.8)
53
+ slop (~> 3.4)
54
+ rack (1.5.2)
55
+ rack-protection (1.5.0)
56
+ rack
57
+ rack-test (0.6.2)
58
+ rack (>= 1.0)
59
+ rake (10.1.0)
60
+ rb-fsevent (0.9.3)
61
+ rb-inotify (0.9.0)
62
+ ffi (>= 0.5.0)
63
+ rb-kqueue (0.2.0)
64
+ ffi (>= 0.5.0)
65
+ rspec (2.14.1)
66
+ rspec-core (~> 2.14.0)
67
+ rspec-expectations (~> 2.14.0)
68
+ rspec-mocks (~> 2.14.0)
69
+ rspec-core (2.14.4)
70
+ rspec-expectations (2.14.0)
71
+ diff-lcs (>= 1.1.3, < 2.0)
72
+ rspec-mocks (2.14.1)
73
+ simple_uuid (0.3.0)
74
+ simplecov (0.7.1)
75
+ multi_json (~> 1.0)
76
+ simplecov-html (~> 0.7.1)
77
+ simplecov-html (0.7.1)
78
+ sinatra (1.4.3)
79
+ rack (~> 1.4)
80
+ rack-protection (~> 1.4)
81
+ tilt (~> 1.3, >= 1.3.4)
82
+ slop (3.4.6)
83
+ thin (1.5.1)
84
+ daemons (>= 1.0.9)
85
+ eventmachine (>= 0.12.6)
86
+ rack (>= 1.0.0)
87
+ thor (0.18.1)
88
+ tilt (1.4.1)
89
+ toystore (0.13.2)
90
+ activemodel (~> 3.0)
91
+ activesupport (~> 3.0)
92
+ adapter (~> 0.7.0)
93
+ simple_uuid (~> 0.2)
94
+
95
+ PLATFORMS
96
+ ruby
97
+
98
+ DEPENDENCIES
99
+ bait!
100
+ bundler (~> 1.3)
101
+ guard-rspec
102
+ pry
103
+ rack-test
104
+ rake
105
+ rspec
106
+ simplecov
@@ -0,0 +1,7 @@
1
+ guard :rspec do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ watch(%r{^lib/.+haml$}) { |m| "spec/lib/bait/api_spec.rb" }
6
+ end
7
+
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Keyvan Fatehi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,121 @@
1
+ Bait
2
+ ====
3
+
4
+ `bait` is a build and integration tester
5
+
6
+ # Backstory
7
+
8
+ https://github.com/DFTi/Scribbeo-motion already is designed in such a way that it vendors our existing iOS (completely written in Objective-C) application https://github.com/DFTi/Critique
9
+
10
+ This gave us a Ruby(Motion) environment where UIAutomator is available using MacBacon (like rspec)
11
+
12
+ As a Ruby on Rails developer, this was invaluable... But the iOS developers we hired disliked it and did not maintain the RubyMotion stuff.
13
+
14
+ Fast forward a year or so later and the app is complex, has no test suite, and our QA testers can barely keep up.
15
+
16
+ Scribbeo-motion proved that this works, and so bait is a service I'm envisioning for as the first stop between Github and the rest of my continuous integration pipeline.
17
+
18
+ # Architectural Overview
19
+
20
+ ```
21
+ Github POST bait:80/
22
+ ______________________ \./
23
+ | Mac OS X 10.8 | |
24
+ | w/ RubyMotion | |
25
+ | ---------------- +---------+----------------+
26
+ | +---+---+ |
27
+ | | API |----[haml]------- you
28
+ | +---+---+ |
29
+ | | |
30
+ | +---------+--+ |
31
+ | |Bait::Build | |
32
+ | +---+-----+--+ |
33
+ | \|/ /|\ |
34
+ | \|/ /|\ |
35
+ | \[build]\ |
36
+ | \|/ /|\ |
37
+ | \|/ /|\ |
38
+ | +------------+ |
39
+ | [.bait/test.sh]------|Bait::Tester| |
40
+ | +------------+ |
41
+ | |
42
+ +-----------------------------------------------+
43
+
44
+ Bait::Build -- Persistent ToyStore
45
+
46
+ Bait::Tester -- Runs your tests and updates build accordingly
47
+ ```
48
+
49
+ *Created with [JavE](http://www.jave.de/)*
50
+
51
+ # Functional Overview
52
+
53
+ ## Github Webhook Support
54
+
55
+ bait provides a Sinatra endpoint for the github push event webhook.
56
+
57
+ When the repo is cloned, an bait executes a file relative to your
58
+ project. This file must exist in order to use bait: `.bait/test.sh`
59
+
60
+ ## .bait/test.sh
61
+
62
+ In this file you will run your test suite. **Be sure to make it
63
+ executable `chmod a+x .bait/test.sh`**
64
+
65
+ This file should output whatever you want to STDOUT/STDERR and return
66
+ the correct exit value.
67
+
68
+ ### Examples
69
+
70
+ #### Ruby / Rails Example (RSpec)
71
+
72
+ ##### [project root]/.bait/test.sh
73
+ ```bash
74
+ #!/bin/bash
75
+ bait_dir=$(dirname $0)
76
+ project_dir="$bait_dir/.."
77
+ cd $project_dir
78
+
79
+ echo "bundling"
80
+ bundle install > /dev/null 2>&1
81
+ bundle exec rspec spec
82
+ ```
83
+
84
+ #### RubyMotion Example
85
+
86
+ ##### [project root]/.bait/test.sh
87
+ ```bash
88
+ #!/bin/bash
89
+ bait_dir=$(dirname $0)
90
+ project_dir="$bait_dir/.."
91
+ cd $project_dir
92
+
93
+ export BUNDLE_GEMFILE=$project_dir/Gemfile
94
+
95
+ echo "bundling"
96
+ bundle install > /dev/null 2>&1
97
+ bundle exec motion-specwrap
98
+ ```
99
+
100
+ There is a bug in RubyMotion where the exit value isn't reported
101
+ properly, that's why we are using
102
+ [motion-specwrap](https://github.com/mdks/motion-specwrap) to run the
103
+ tests and report the correct exit value
104
+
105
+ ## Objective-C ?
106
+
107
+ So you can see how bait will run any test suite via arbitrary bash
108
+ scripts upon a Github hook.
109
+
110
+ But how exactly will it help add a ruby test suite to an Obj-C app?
111
+
112
+ Watch this spot for some examples soon; essentially we'll be doing this
113
+ in Ruby using bait::Wrap::ObjC or some such :)
114
+
115
+ # Future
116
+
117
+ ## Static Code Analysis
118
+
119
+ Integrate [metric-fu](http://metric-fu.rubyforge.org/) for ruby apps and [OCLint](http://oclint.org/) for objective-c apps. Report these in Redis.
120
+
121
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bait/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "bait"
8
+ spec.version = Bait::VERSION
9
+ spec.authors = ["Keyvan Fatehi"]
10
+ spec.email = ["keyvan@digitalfilmtree.com"]
11
+ spec.description = %q{build and integration test service}
12
+ spec.summary = %q{build and integration test service}
13
+ spec.homepage = "https://github.com/keyvanfatehi/bait"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "guard-rspec"
25
+ spec.add_development_dependency "pry"
26
+ spec.add_development_dependency 'rack-test'
27
+ spec.add_development_dependency 'simplecov'
28
+
29
+ spec.add_runtime_dependency "sinatra"
30
+ spec.add_runtime_dependency "moneta"
31
+ spec.add_runtime_dependency "toystore"
32
+ spec.add_runtime_dependency "git"
33
+ spec.add_runtime_dependency "haml"
34
+ spec.add_runtime_dependency "thin"
35
+ end
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ ENV['RACK_ENV'] = 'production'
3
+ require File.expand_path("../../config/boot.rb", __FILE__)
4
+ Bait::Api.run!
5
+
@@ -0,0 +1,8 @@
1
+ ENV["RACK_ENV"] ||= "development"
2
+
3
+ require 'rubygems' unless defined?(Gem)
4
+ require 'bundler/setup'
5
+ Bundler.require(:default, ENV["RACK_ENV"].to_sym)
6
+
7
+ Dir["./lib/**/*.rb"].each { |f| require f }
8
+
@@ -0,0 +1,38 @@
1
+ require "bait/version"
2
+ require 'moneta'
3
+ require 'fileutils'
4
+
5
+ module Bait
6
+ def self.storage_dir
7
+ path = File.join("#{self.home}", "#{self.env}")
8
+ FileUtils.mkdir_p path
9
+ path
10
+ end
11
+
12
+ def self.db_dir
13
+ db_dir = File.join self.storage_dir, "databases"
14
+ FileUtils.mkdir_p db_dir
15
+ db_dir
16
+ end
17
+
18
+ def self.db_file name
19
+ yaml_file = File.join self.db_dir, "#{name}.yaml"
20
+ FileUtils.touch yaml_file
21
+ yaml_file
22
+ end
23
+
24
+ def self.store
25
+ @store ||= begin
26
+ Moneta.new :YAML, :file => db_file("main")
27
+ end
28
+ end
29
+
30
+ def self.env
31
+ ENV['RACK_ENV'] ||= 'production'
32
+ end
33
+
34
+ def self.home
35
+ File.join Etc.getpwuid.dir, '.bait'
36
+ end
37
+ end
38
+
@@ -0,0 +1,43 @@
1
+ require 'sinatra'
2
+ require 'haml'
3
+ require 'json'
4
+ require 'bait/build'
5
+
6
+ module Bait
7
+ class Api < Sinatra::Base
8
+ set :port, 8417
9
+
10
+ get '/' do
11
+ redirect '/build'
12
+ end
13
+
14
+ post '/' do
15
+ if params && params["payload"]
16
+ push = JSON.parse(params["payload"])
17
+ Build.create({
18
+ name: push["repository"]["name"],
19
+ clone_url: push["repository"]["url"],
20
+ owner_name: push["repository"]["owner"]["name"],
21
+ owner_email: push["repository"]["owner"]["email"],
22
+ ref: push["ref"]
23
+ }).test_later
24
+ end
25
+ end
26
+
27
+ get '/build' do
28
+ @builds = Bait::Build.all
29
+ haml :builds
30
+ end
31
+
32
+ post '/build/create' do
33
+ build = Build.create(clone_url:params["clone_url"], name:'test')
34
+ build.test_later
35
+ redirect '/build'
36
+ end
37
+
38
+ get '/build/remove/:id' do
39
+ Build.destroy params["id"]
40
+ redirect '/build'
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,50 @@
1
+ require 'bait'
2
+ require 'moneta'
3
+ require "toystore"
4
+ require 'bait/simple_query'
5
+ require 'bait/tester'
6
+
7
+ module Bait
8
+ class Build
9
+ include Toy::Store
10
+ extend Bait::SimpleQuery
11
+
12
+ @@db_file = Bait.db_file('builds')
13
+ adapter :memory, Moneta.new(:YAML, :file => @@db_file)
14
+
15
+ attribute :ref, String
16
+ attribute :owner_name, String
17
+ attribute :owner_email, String
18
+ attribute :name, String
19
+ attribute :clone_url, String
20
+ attribute :passed, Boolean
21
+ attribute :output, String, default: ""
22
+ attribute :tested, Boolean, default: false
23
+
24
+ validates_presence_of :name
25
+ validates_presence_of :clone_url
26
+
27
+ def tester
28
+ @tester ||= Bait::Tester.new(self)
29
+ end
30
+
31
+ def test_later
32
+ self.tested = false
33
+ self.save
34
+ unless Bait.env == "test"
35
+ fork do
36
+ self.tester.clone!
37
+ self.tester.test!
38
+ end
39
+ end
40
+ self
41
+ end
42
+
43
+ after_destroy :remove_sandbox
44
+
45
+ def remove_sandbox
46
+ tester.cleanup!
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,11 @@
1
+ /*
2
+ YUI 3.11.0 (build d549e5c)
3
+ Copyright 2013 Yahoo! Inc. All rights reserved.
4
+ Licensed under the BSD License.
5
+ http://yuilibrary.com/license/
6
+ */
7
+
8
+ html{color:#000;background:#FFF}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0}table{border-collapse:collapse;border-spacing:0}fieldset,img{border:0}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal}ol,ul{list-style:none}caption,th{text-align:left}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal}q:before,q:after{content:''}abbr,acronym{border:0;font-variant:normal}sup{vertical-align:text-top}sub{vertical-align:text-bottom}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit}input,textarea,select{*font-size:100%}legend{color:#000}#yui3-css-stamp.cssreset{display:none}
9
+
10
+
11
+
@@ -0,0 +1,30 @@
1
+ module Bait
2
+ module SimpleQuery
3
+ def self.extended(base)
4
+ base.after_create do
5
+ id_list = self.class.ids
6
+ id_list << self.id
7
+ self.class.ids = id_list
8
+ end
9
+ base.after_destroy do
10
+ self.class.ids = self.class.ids.reject{|id| id == self.id}
11
+ end
12
+ end
13
+ def ids
14
+ Bait.store.raw["build_ids"] ||= []
15
+ end
16
+
17
+ def ids=(new_ids)
18
+ Bait.store.raw["build_ids"] = new_ids
19
+ end
20
+
21
+ def all
22
+ ids.map{|id| self.read(id)}
23
+ end
24
+
25
+ def last
26
+ self.read(self.ids.last)
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,73 @@
1
+ require 'bait'
2
+ require 'bait/build'
3
+ require 'git'
4
+ require 'pty'
5
+
6
+ module Bait
7
+ class Tester
8
+ attr_reader :passed
9
+
10
+ def initialize build
11
+ @build = build
12
+ end
13
+
14
+ def bait_dir
15
+ File.join(clone_path, ".bait")
16
+ end
17
+
18
+ def script
19
+ File.join(bait_dir, "test.sh")
20
+ end
21
+
22
+ def test!
23
+ begin
24
+ begin
25
+ PTY.spawn(script) do |r, w, pid|
26
+ r.each { |line| @build.output << line }
27
+ @build.passed = PTY.check(pid).exitstatus == 0
28
+ end
29
+ rescue PTY::ChildExited => e
30
+ @build.output << e
31
+ @build.passed = false
32
+ end
33
+ @build.tested = true
34
+ rescue Errno::ENOENT => ex
35
+ @build.output << "A test script was expected but missing.\nError: #{ex.message}"
36
+ ensure
37
+ @build.save
38
+ end
39
+ end
40
+
41
+ def clone_path
42
+ File.join(sandbox_directory, @build.name)
43
+ end
44
+
45
+ def clone!
46
+ unless cloned?
47
+ begin
48
+ Git.clone(@build.clone_url, @build.name, :path => sandbox_directory)
49
+ rescue => ex
50
+ msg = "Failed to clone #{@build.clone_url}"
51
+ puts msg
52
+ @build.stderr = "#{msg}\n\n#{ex.message}\n\n#{ex.backtrace}"
53
+ @build.save
54
+ end
55
+ end
56
+ end
57
+
58
+ def cloned?
59
+ Dir.exists? File.join(clone_path, ".git/")
60
+ end
61
+
62
+ def cleanup!
63
+ FileUtils.rm_rf sandbox_directory
64
+ end
65
+
66
+ def sandbox_directory
67
+ @sandbox_directory ||= begin
68
+ dir = File.join Bait.storage_dir, "build_tester", @build.id
69
+ FileUtils.mkdir_p(dir) && dir
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,3 @@
1
+ module Bait
2
+ VERSION = "0.2.0"
3
+ end
@@ -0,0 +1,31 @@
1
+ Listing Builds
2
+ %ul
3
+ - @builds.each do |build|
4
+ %li
5
+ %hr
6
+ .name= build.name
7
+ %a.clone_url{href:build.clone_url}= build.clone_url
8
+ .ref= build.ref
9
+ .status
10
+ Status:
11
+ - if build.tested?
12
+ = build.passed? ? "Passed!" : "Failed!"
13
+ - else
14
+ Queued
15
+ .output
16
+ Output:
17
+ %pre= build.output
18
+ .actions
19
+ %a{href:"/build/remove/#{build.id}"} Remove
20
+
21
+ %hr
22
+
23
+ .manual_clone
24
+ To clone manually without waiting for a post-receive hook, enter the following and hit submit
25
+ %form{ :action => "/build/create", :method => "post"}
26
+ %fieldset
27
+ %ol
28
+ %li
29
+ %label{:for => "clone_url"} clone_url:
30
+ %input{:type => "text", :name => "clone_url", :class => "text"}
31
+ %input{:type => "submit", :value => "Go", :class => "button"}
@@ -0,0 +1,11 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %title Bait
5
+ %link{rel:'stylesheet', href:'css/styles.css'}
6
+ %body
7
+ #nav
8
+ %a{href:'/'}Home
9
+ #content
10
+ = yield
11
+
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+ require 'bait/api'
3
+ require 'bait/build'
4
+
5
+ describe "Sinatra App" do
6
+ let(:app) { Bait::Api }
7
+ subject { last_response }
8
+
9
+ describe "github post-receive hook" do
10
+ let(:github_json) do
11
+ <<-GITHUB_JSON
12
+ {
13
+ "before": "5aef35982fb2d34e9d9d4502f6ede1072793222d",
14
+ "repository": {
15
+ "url": "http://github.com/defunkt/github",
16
+ "name": "github",
17
+ "owner": {
18
+ "email": "chris@ozmm.org",
19
+ "name": "defunkt"
20
+ }
21
+ },
22
+ "commits": {
23
+ "41a212ee83ca127e3c8cf465891ab7216a705f59": {
24
+ "url": "http://github.com/defunkt/github/commit/41a212ee83ca127e3c8cf465891ab7216a705f59",
25
+ "author": {
26
+ "email": "chris@ozmm.org",
27
+ "name": "Chris Wanstrath"
28
+ },
29
+ "message": "okay i give in",
30
+ "timestamp": "2008-02-15T14:57:17-08:00"
31
+ },
32
+ "de8251ff97ee194a289832576287d6f8ad74e3d0": {
33
+ "url": "http://github.com/defunkt/github/commit/de8251ff97ee194a289832576287d6f8ad74e3d0",
34
+ "author": {
35
+ "email": "chris@ozmm.org",
36
+ "name": "Chris Wanstrath"
37
+ },
38
+ "message": "update pricing a tad",
39
+ "timestamp": "2008-02-15T14:36:34-08:00"
40
+ }
41
+ },
42
+ "after": "de8251ff97ee194a289832576287d6f8ad74e3d0",
43
+ "ref": "refs/heads/master"
44
+ }
45
+ GITHUB_JSON
46
+ end
47
+
48
+ let (:build) { Bait::Build.last }
49
+
50
+ describe "POST /" do
51
+ before do
52
+ post '/', payload: github_json
53
+ end
54
+
55
+ it { should be_ok }
56
+
57
+ it "creates a build" do
58
+ build.name.should eq "github"
59
+ build.owner_name.should eq "defunkt"
60
+ build.owner_email.should eq "chris@ozmm.org"
61
+ build.ref.should eq "refs/heads/master"
62
+ end
63
+
64
+ it "will be tested later based on a flag" do
65
+ build.should_not be_tested
66
+ end
67
+ end
68
+
69
+ describe "GET /" do
70
+ before { get '/' }
71
+ it { should be_redirect }
72
+ end
73
+
74
+ describe "GET /build" do
75
+ before do
76
+ Bait::Build.create(name: "quickfox", clone_url:'...')
77
+ Bait::Build.create(name: "slowsloth", clone_url:'...')
78
+ get '/build'
79
+ end
80
+
81
+ it { should be_ok }
82
+
83
+ it "shows the builds" do
84
+ subject.body.should match /quickfox/
85
+ subject.body.should match /slowsloth/
86
+ end
87
+ end
88
+
89
+ describe "POST /build/create" do
90
+ let(:test_url){ "http://github.com/defunkt/github" }
91
+ it "can create a build manually" do
92
+ post '/build/create', {clone_url:test_url}
93
+ build.clone_url.should eq test_url
94
+ end
95
+ end
96
+
97
+ describe "GET /build/remove/#" do
98
+ before do
99
+ @build = Bait::Build.create(name: "quickfox", clone_url:'...')
100
+ @sandbox = @build.tester.sandbox_directory
101
+ end
102
+ it "removes the build from store and its files from the filesystem" do
103
+ Bait::Build.ids.should have(1).item
104
+ get "/build/remove/#{@build.id}"
105
+ expect{@build.reload}.to raise_error Toy::NotFound
106
+ Bait::Build.ids.should be_empty
107
+ Pathname.new(@sandbox).should_not exist
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+ require 'bait/build'
3
+
4
+ describe Bait::Build do
5
+ subject { Bait::Build }
6
+
7
+ describe ".all" do
8
+ context "with nothing in the store" do
9
+ specify { subject.all.should be_empty }
10
+ end
11
+
12
+ context "with builds in the store" do
13
+ before do
14
+ subject.create(name: "foo", clone_url:'...')
15
+ subject.create(name: "bar", clone_url:'...')
16
+ end
17
+
18
+ specify { subject.all.should have(2).items }
19
+
20
+ it "returns the builds with data" do
21
+ subject.all[0].name.should eq "foo"
22
+ subject.all[1].name.should eq "bar"
23
+ end
24
+ end
25
+ end
26
+
27
+ describe ".last" do
28
+ context "with nothing in the store" do
29
+ specify { subject.last.should be_nil }
30
+ end
31
+ context "with builds in the store" do
32
+ before do
33
+ subject.create(name: "fud", clone_url:'...')
34
+ @build = subject.create(name: "baz", clone_url:'...')
35
+ end
36
+ it "returns the last created build" do
37
+ subject.last.name.should eq "baz"
38
+ end
39
+ end
40
+ end
41
+
42
+ let (:build) { Bait::Build.create(name: "app", clone_url:'...') }
43
+
44
+ describe "#tester" do
45
+ specify { build.tester.should be_a Bait::Tester }
46
+ end
47
+
48
+ describe "#passed" do
49
+ it "starts as nil" do
50
+ build.passed.should be_nil
51
+ end
52
+ end
53
+
54
+ describe "removal" do
55
+ before do
56
+ @build = build
57
+ end
58
+
59
+ it "is removed from build ids" do
60
+ Bait::Build.ids.should have(1).item
61
+ @build.destroy
62
+ Bait::Build.ids.should be_empty
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+ require 'bait/tester'
3
+
4
+ describe Bait::Tester do
5
+ let(:repo_path) do
6
+ path = File.join(File.dirname(__FILE__), '..', '..', '..')
7
+ File.expand_path(path)
8
+ end
9
+ let(:build) { Bait::Build.create(name: "bait", clone_url:repo_path) }
10
+ let(:tester) { build.tester }
11
+
12
+ describe "#sandbox_directory" do
13
+ it "creates a directory on disk" do
14
+ Dir.exists?(tester.sandbox_directory).should be_true
15
+ end
16
+
17
+ it "is beneath Bait storage directory" do
18
+ tester.sandbox_directory.should match Bait.storage_dir
19
+ end
20
+ end
21
+
22
+ describe "#cloned?" do
23
+ specify { tester.should_not be_cloned }
24
+ end
25
+
26
+ describe "#clone!" do
27
+ before { tester.clone! }
28
+ specify { tester.should be_cloned }
29
+ end
30
+
31
+ describe "#test!" do
32
+ shared_examples_for "a test run" do
33
+ it "saves output into the build" do
34
+ build.output.should match "this is a test script"
35
+ end
36
+ it "is marked as tested" do
37
+ build.should be_tested
38
+ end
39
+ end
40
+
41
+ before do
42
+ tester.clone!
43
+ end
44
+
45
+ subject { build.reload }
46
+
47
+ context "does not have a test script" do
48
+ before do
49
+ FileUtils.rm tester.script
50
+ tester.test!
51
+ end
52
+ it { should_not be_tested }
53
+ it "has Bait errors in output" do
54
+ subject.output.should match /script was expected but missing/
55
+ end
56
+ end
57
+
58
+ context "has a test script" do
59
+ context "successful" do
60
+ before do
61
+ write_script_with_status tester.script, 0
62
+ tester.test!
63
+ end
64
+ it { should be_passed }
65
+ it_behaves_like "a test run"
66
+ end
67
+ context 'failure' do
68
+ before do
69
+ write_script_with_status tester.script, 1
70
+ tester.test!
71
+ end
72
+ it { should_not be_passed }
73
+ it_behaves_like "a test run"
74
+ end
75
+ end
76
+ end
77
+
78
+ describe "cleanup!" do
79
+ before do
80
+ tester.clone!
81
+ end
82
+ it "removes the entire sandbox" do
83
+ Dir.exists?(tester.sandbox_directory).should be_true
84
+ tester.cleanup!
85
+ Dir.exists?(tester.sandbox_directory).should be_false
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bait do
4
+ it 'should have a version number' do
5
+ Bait::VERSION.should_not be_nil
6
+ end
7
+ end
@@ -0,0 +1,26 @@
1
+ ENV['RACK_ENV'] = "test"
2
+ require 'simplecov'
3
+ SimpleCov.start
4
+
5
+ require 'bait'
6
+ require 'fileutils'
7
+
8
+ def clear_storage
9
+ FileUtils.rm_rf Bait.storage_dir
10
+ end
11
+
12
+ def clear_db
13
+ Dir.glob(File.join(Bait.db_dir, "*")).map do |f|
14
+ File.open(f, 'w')
15
+ end
16
+ end
17
+
18
+ require 'support/script_maker'
19
+ require 'rack/test'
20
+
21
+ RSpec.configure do |config|
22
+ config.before(:suite) { clear_storage }
23
+ config.before(:each) { clear_db }
24
+ config.include Bait::SpecHelpers::ScriptMaker
25
+ config.include Rack::Test::Methods
26
+ end
@@ -0,0 +1,14 @@
1
+ module Bait
2
+ module SpecHelpers
3
+ module ScriptMaker
4
+ def write_script_with_status script, status
5
+ File.open(script, "w") do |f|
6
+ f.puts "#!/usr/bin/env bash"
7
+ f.puts "echo this is a test script"
8
+ f.puts "exit #{status.to_i}"
9
+ end
10
+ File.chmod(0755, script)
11
+ end
12
+ end
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,260 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bait
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Keyvan Fatehi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-07-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard-rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rack-test
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: sinatra
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: moneta
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: toystore
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: git
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - '>='
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: haml
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - '>='
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: thin
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - '>='
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :runtime
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - '>='
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ description: build and integration test service
196
+ email:
197
+ - keyvan@digitalfilmtree.com
198
+ executables:
199
+ - bait
200
+ extensions: []
201
+ extra_rdoc_files: []
202
+ files:
203
+ - .bait/test.sh
204
+ - .gitignore
205
+ - DEVELOP
206
+ - Gemfile
207
+ - Gemfile.lock
208
+ - Guardfile
209
+ - LICENSE
210
+ - README.md
211
+ - Rakefile
212
+ - bait.gemspec
213
+ - bin/bait
214
+ - config/boot.rb
215
+ - lib/bait.rb
216
+ - lib/bait/api.rb
217
+ - lib/bait/build.rb
218
+ - lib/bait/public/css/styles.css
219
+ - lib/bait/simple_query.rb
220
+ - lib/bait/tester.rb
221
+ - lib/bait/version.rb
222
+ - lib/bait/views/builds.haml
223
+ - lib/bait/views/layout.haml
224
+ - spec/lib/bait/api_spec.rb
225
+ - spec/lib/bait/build_spec.rb
226
+ - spec/lib/bait/tester_spec.rb
227
+ - spec/lib/bait_spec.rb
228
+ - spec/spec_helper.rb
229
+ - spec/support/script_maker.rb
230
+ homepage: https://github.com/keyvanfatehi/bait
231
+ licenses:
232
+ - MIT
233
+ metadata: {}
234
+ post_install_message:
235
+ rdoc_options: []
236
+ require_paths:
237
+ - lib
238
+ required_ruby_version: !ruby/object:Gem::Requirement
239
+ requirements:
240
+ - - '>='
241
+ - !ruby/object:Gem::Version
242
+ version: '0'
243
+ required_rubygems_version: !ruby/object:Gem::Requirement
244
+ requirements:
245
+ - - '>='
246
+ - !ruby/object:Gem::Version
247
+ version: '0'
248
+ requirements: []
249
+ rubyforge_project:
250
+ rubygems_version: 2.0.3
251
+ signing_key:
252
+ specification_version: 4
253
+ summary: build and integration test service
254
+ test_files:
255
+ - spec/lib/bait/api_spec.rb
256
+ - spec/lib/bait/build_spec.rb
257
+ - spec/lib/bait/tester_spec.rb
258
+ - spec/lib/bait_spec.rb
259
+ - spec/spec_helper.rb
260
+ - spec/support/script_maker.rb