vet-ci 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .vetci
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in vet-ci.gemspec
4
+ gemspec
@@ -0,0 +1,23 @@
1
+ VetCI
2
+ -----
3
+
4
+ VetCI makes it easy to take care of your code pets. It's a barebones continuous integration system that lets you run commands (usually testing or building) against as many projects as you need. It's heavily inspired by CIJoe, but is designed to be a bit more flexible.
5
+
6
+ This documentation isn't even close to complete. It will be a mess for a while until version 1.
7
+
8
+
9
+ The Vetfile
10
+ -----------
11
+
12
+ Your project's Vetfile is a crucial piece of the VetCI system. This file describes your system's overall structure to VetCI and explains how tests should be run. VetCI is very flexible regarding which test framework(s) you choose to use - the only requirement is that your test suite return 0 on success and some other integer on fail.
13
+
14
+
15
+ If you have a single project, you can include a Vetfile in the project root directory (at the same level as your Gemfile for Ruby or your package.json for Node).
16
+
17
+ Vetfile Options
18
+
19
+ name The display name of your project.
20
+ path The path to your project, relative to the Vetfile.
21
+ command The build/test command that should be run on your project.
22
+ autoupdate When set to true, automatically performs a git fetch origin and hard reset on the code prior to running the tests. Defaults to true.
23
+ default_branch Specifies the default branch VetCI should try to work with.
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
data/Vetfile ADDED
@@ -0,0 +1,5 @@
1
+ projects:
2
+ - name: vet-ci
3
+ command: rspec spec
4
+ autoupdate: false
5
+ default_branch: master
@@ -0,0 +1 @@
1
+ # Vetfiles are now yaml...
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
4
+
5
+ require 'vet-ci'
6
+ @vetci = VetCI::Core.new
7
+ @vetci.start
@@ -0,0 +1,20 @@
1
+ require 'vet-ci/version'
2
+ require 'vet-ci/project'
3
+ require 'vet-ci/build'
4
+ require 'vet-ci/setup'
5
+ require 'vet-ci/server'
6
+ require 'yaml'
7
+ require 'grit'
8
+
9
+ module VetCI
10
+ class Core
11
+ attr_accessor :vetfile
12
+ def initialize
13
+ self.vetfile = VetCI::Setup.go
14
+ end
15
+
16
+ def start
17
+ VetCI::Server.start
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,40 @@
1
+ module VetCI
2
+ class Build
3
+ attr_accessor :id,
4
+ :output,
5
+ :status,
6
+ :date,
7
+ :commit,
8
+ :committer,
9
+ :payload,
10
+ :project
11
+
12
+
13
+ def initialize(attributes = {})
14
+ attributes.each do |key, value|
15
+ self.send("#{key}=", value)
16
+ end
17
+ end
18
+
19
+ def commit_info
20
+ commit = self.project.repo.commit(self.commit)
21
+ commit.nil? ? nil : commit.to_hash
22
+ end
23
+
24
+ def status_class
25
+ if self.status == 0
26
+ 'passed'
27
+ else
28
+ 'failed'
29
+ end
30
+ end
31
+
32
+ def friendly_time
33
+ self.date.strftime('%A, %B %e, %Y at %I:%M %p')
34
+ end
35
+
36
+ def dashboard_time
37
+ self.date.strftime('%D @ %r')
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,197 @@
1
+ require 'pstore'
2
+ require 'vet-ci/util'
3
+ require 'vet-ci/build'
4
+ require 'grit'
5
+ require 'json'
6
+
7
+ module VetCI
8
+ class Project
9
+
10
+ class << self
11
+ # This shouldn't be here,
12
+ # but it was easy. We'll refactor it
13
+ # when that time comes...
14
+
15
+ def projects
16
+ @projects ||= {}
17
+ end
18
+
19
+ def projects=(value)
20
+ @projects = value
21
+ end
22
+
23
+ def datastore
24
+ @datastore
25
+ end
26
+
27
+ def datastore=(value)
28
+ @datastore = value
29
+ end
30
+
31
+ #Returns a project in the list that is named ..
32
+ def named(value)
33
+ self.projects[value]
34
+ end
35
+
36
+ def parse_vetfile_contents(parameters)
37
+ parameters.each do |project|
38
+ name = project['name']
39
+ path = project['path']
40
+ command = project['command']
41
+ default_branch = project['default_branch']
42
+ autoupdate = (project['autoupdate'].nil? ? false : project['autoupdate'])
43
+
44
+ if path.nil?
45
+ path = Dir.pwd
46
+ else
47
+ path = File.expand_path path
48
+ end
49
+
50
+ project = Project.new(:name => name, :project_path => path, :build_command => command, :default_branch => default_branch, :autoupdate => autoupdate)
51
+
52
+ self.projects[project.name] = project
53
+ end
54
+ end
55
+ end
56
+
57
+ attr_accessor :name
58
+ attr_accessor :project_path
59
+ attr_accessor :build_command
60
+ attr_accessor :default_branch
61
+ attr_accessor :autoupdate
62
+ attr_accessor :building
63
+
64
+
65
+ def builds
66
+ @builds ||= []
67
+ end
68
+
69
+ def pagedBuilds(page=1, limit=10)
70
+ # We need to calculate the slice
71
+ page = page.nil? ? 1 : page.to_i
72
+ limit = limit.nil? ? 10 : limit.to_i
73
+ page = 1 if page < 0
74
+ start = (page-1) * limit
75
+ # limit is the length
76
+ result = self.builds[start,limit]
77
+ result.nil? ? [] : result
78
+ end
79
+
80
+ def builds=(value)
81
+ @builds = value
82
+ end
83
+
84
+ def insertBuild(build)
85
+ self.builds << build
86
+ @builds.sort! do |a, b|
87
+ if a.date == b.date
88
+ 0
89
+ elsif a.date < b.date
90
+ 1
91
+ else
92
+ -1
93
+ end
94
+ end
95
+ end
96
+
97
+ def initialize(attributes = {})
98
+ attributes.each do |key, value|
99
+ self.send("#{key}=", value)
100
+ end
101
+
102
+ # Now, let's load any existing builds in the list from the datastore
103
+ unless Project.datastore.nil?
104
+ Project.datastore.transaction(true) do
105
+ builds = Project.datastore[self.name]
106
+ self.builds.concat(builds) unless builds.nil?
107
+ end
108
+ end
109
+ end
110
+
111
+ def save!
112
+ unless Project.datastore.nil?
113
+ Project.datastore.transaction do
114
+ Project.datastore[self.name] = @builds
115
+ end
116
+ end
117
+ end
118
+
119
+ def git_update
120
+ reset_branch = self.default_branch.nil? ? "" : " origin/#{self.default_branch}"
121
+ `cd #{self.project_path} && git fetch origin && git reset --hard#{reset_branch}`
122
+ end
123
+
124
+ def repo
125
+ Grit::Repo.new(self.project_path)
126
+ end
127
+
128
+ def is_building?
129
+ self.building == true
130
+ end
131
+
132
+ def last_build_status
133
+ if is_building?
134
+ return 'running'
135
+ elsif self.builds.count > 0
136
+ return self.builds.first.status_class
137
+ else
138
+ return ''
139
+ end
140
+ end
141
+
142
+ def build(faye=nil, payload=nil)
143
+ if is_building?
144
+ return
145
+ end
146
+
147
+ if payload
148
+ begin
149
+ payload = JSON.parse payload
150
+ rescue
151
+ payload = nil
152
+ end
153
+ end
154
+
155
+ Thread.new {build!(faye, payload)}
156
+ end
157
+
158
+ def build!(faye=nil, payload=nil)
159
+ self.building = true
160
+
161
+ unless faye.nil?
162
+ faye.publish '/all', :project => self.name, :status => 'running'
163
+ end
164
+
165
+ # First, let's reset the repo if we said we should in the Vetfile
166
+ if self.autoupdate == true
167
+ puts "Updating the repo"
168
+ self.git_update
169
+ end
170
+
171
+ @result = ''
172
+ repo = Grit::Repo.new @project_path
173
+ commit = repo.commits.first
174
+ Util.open_pipe("cd #{@project_path} && #{@build_command} 2>&1") do |pipe, process_id|
175
+ puts "#{Time.now.to_i}: Building with command '#{@build_command}'..."
176
+ @current_pid = process_id
177
+ @result = pipe.read
178
+ end
179
+ Process.waitpid(@current_pid)
180
+ puts $?.exitstatus.to_i
181
+ if commit.nil?
182
+ current_build = Build.new(:project => self, :status => $?.exitstatus.to_i, :output => @result, :date => Time.now, :payload => payload)
183
+ else
184
+ current_build = Build.new(:project => self, :status => $?.exitstatus.to_i, :output => @result, :date => Time.now, :commit => commit.id, :committer => commit.committer.name, :payload => payload)
185
+ end
186
+
187
+ puts "Saving build..."
188
+ unless faye.nil?
189
+ faye.publish '/all', :project => self.name, :status => current_build.status_class, :last_build => current_build.dashboard_time
190
+ end
191
+
192
+ self.insertBuild current_build
193
+ self.building = false
194
+ self.save!
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,264 @@
1
+ /* VetCI Base Styles */
2
+ html {
3
+ font-family: 'Helvetica Neue', sans-serif;
4
+ }
5
+
6
+ body {
7
+ width: 960px;
8
+ margin: auto;
9
+ }
10
+
11
+ .logo {
12
+ float: left;
13
+ }
14
+
15
+ .logo a {
16
+ color: #000;
17
+ text-decoration: none;
18
+ }
19
+
20
+ .build_all {
21
+ float: right;
22
+ width: 100px;
23
+ height: 40px;
24
+ line-height: 40px;
25
+ -webkit-appearance: none;
26
+ border: none;
27
+ padding: 0;
28
+ margin: 0;
29
+ position: relative;
30
+ top: 25px;
31
+ font-weight: bold;
32
+ color: #999;
33
+ font-size: 16px;
34
+ background-color: #ddd;
35
+ -webkit-border-radius: 5px;
36
+ text-shadow: 0px 1px 0px #fff;
37
+ }
38
+
39
+ .build_all:hover {
40
+ cursor: pointer;
41
+ }
42
+
43
+ #main {
44
+ clear: both;
45
+ }
46
+
47
+ .project .status.passed, .build .status.passed {
48
+ background-color: #0f0;
49
+ }
50
+
51
+ .project .status.failed, .build .status.failed {
52
+ background-color: #f00;
53
+ }
54
+
55
+ .project .status.running, .build .status.running {
56
+ background-color: #FDD617;
57
+ }
58
+
59
+ /* LANDING PAGE */
60
+ .link_wrap {
61
+ text-decoration: none;
62
+ display: block;
63
+ }
64
+
65
+ .project {
66
+ width: 450px;
67
+ height: 75px;
68
+ background-color: #ddd;
69
+ -webkit-border-radius:5px;
70
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#ddd), to(#ccc));
71
+ background-image: -webkit-linear-gradient(top, #ddd, #ccc);
72
+ border: 1px solid #ccc;
73
+ -webkit-box-shadow: 0px 0px 5px #bbb;
74
+ box-shadow: 0px 0px 5px #bbb;
75
+ position: relative;
76
+ overflow: hidden;
77
+ margin: 0px 15px 15px 0px;
78
+ }
79
+
80
+ .project h2 {
81
+ margin: 0px;
82
+ padding: 0px;
83
+ color: #666;
84
+ text-shadow: 0px 1px 0px #eee;
85
+ position: relative;
86
+ top: 14px;
87
+ left: 10px;
88
+ width: 400px;
89
+ font-size: 24px;
90
+ height: 26px;
91
+ overflow: hidden;
92
+ }
93
+
94
+ .project .status, .build .status {
95
+ position: absolute;
96
+ display: block;
97
+ height: 26px;
98
+ width: 26px;
99
+ background-color: #bbb;
100
+ top: 15px;
101
+ right: 10px;
102
+ border-radius: 13px;
103
+ -webkit-border-radius: 13px;
104
+ -moz-border-radius: 13px;
105
+ -webkit-box-shadow: 0px 0px 10px #333 inset;
106
+ }
107
+
108
+ .project .tab_button {
109
+ text-align: center;
110
+ position: absolute;
111
+ color: #888;
112
+ width: 60px;
113
+ display: block;
114
+ height: 18px;
115
+ line-height: 18px;
116
+ font-size: 12px;
117
+ font-weight: bold;
118
+ border-left: 1px inset #eee;
119
+ text-shadow: 0px 1px 0px #ccc;
120
+ text-decoration:none;
121
+ -webkit-transition: color .25s linear;
122
+ }
123
+
124
+ .project .tab_button:hover {
125
+ color: #666;
126
+ }
127
+
128
+ .project .build_project {
129
+ bottom: 0;
130
+ right: 0;
131
+ }
132
+ .project .view_project {
133
+ bottom: 0;
134
+ right: 60px;
135
+ }
136
+
137
+ .project .last_build {
138
+ position: absolute;
139
+ height: 18px;
140
+ line-height: 18px;
141
+ font-size: 12px;
142
+ bottom: 0;
143
+ left: 10px;
144
+ color: #888;
145
+ text-shadow: 0px 1px 0px #ccc;
146
+ }
147
+
148
+ .project .bottom_tab {
149
+ position: absolute;
150
+ width: 450px;
151
+ height: 18px;
152
+ background-color: #bbb;
153
+ -webkit-box-shadow: 0px 2px 3px #aaa inset;
154
+ bottom: 0;
155
+ left: 0;
156
+ -webkit-border-bottom-left-radius: 5px;
157
+ -webkit-border-bottom-right-radius: 5px;
158
+ }
159
+
160
+
161
+
162
+ /* BUILD PAGE */
163
+
164
+ .build {
165
+ position: relative;
166
+ background-color: #ddd;
167
+ -webkit-border-radius: 5px;
168
+ -moz-border-radius: 5px;
169
+ border-radius: 5px;
170
+ padding: 0px;
171
+ margin-bottom: 15px;
172
+ border: 1px solid #ccc;
173
+ -webkit-box-shadow: 0px 0px 5px #bbb;
174
+ box-shadow: 0px 0px 5px #bbb;
175
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#ddd), to(#ccc));
176
+ background-image: -webkit-linear-gradient(top, #ddd, #ccc);
177
+ background-image: -moz-linear-gradient(top, #ddd, #ccc);
178
+ background-image: -ms-linear-gradient(top, #ddd, #ccc);
179
+ background-image: -o-linear-gradient(top, #ddd, #ccc);
180
+ background-image: linear-gradient(top, #ddd, #ccc);
181
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#ddd', EndColorStr='#ccc');
182
+ }
183
+
184
+ .build .delete {
185
+ position: absolute;
186
+ top: 5px;
187
+ right: 10px;
188
+ color: white;
189
+ font-weight: bold;
190
+ text-decoration: none;
191
+ background-color: #bbb;
192
+ height: 15px;
193
+ width: 15px;
194
+ text-align: center;
195
+ line-height: 15px;
196
+ border-radius: 8px;
197
+ box-shadow: 0px 0px 1px #555 inset;
198
+ font-size: 10px;
199
+ color: #eee;
200
+ text-shadow: 0px 1px 0px #777;
201
+ }
202
+
203
+ .build .type {
204
+ position: absolute;
205
+ top: 5px;
206
+ right: 5px;
207
+ color: #999;
208
+ text-shadow: 0px 1px 0px #fff;
209
+ }
210
+
211
+ .build .title {
212
+ margin: 0;
213
+ padding: 10px;
214
+ color: #666;
215
+ text-shadow: 0px 1px 0px #eee;
216
+ font-weight: bold;
217
+ }
218
+
219
+
220
+ .build .results_toggle {
221
+ margin: 0;
222
+ padding: 0;
223
+ color: #eee;
224
+ text-align: center;
225
+ font-size: 10px;
226
+ }
227
+
228
+ .build .results_toggle:hover {
229
+ cursor: pointer;
230
+ }
231
+
232
+ .build .results {
233
+ display: none;
234
+ }
235
+
236
+ .build_details {
237
+ color: #666;
238
+ text-shadow: 0px 1px 0px #fff;
239
+ padding: 5px 10px;
240
+ }
241
+
242
+ .build_details .label {
243
+ display: inline-block;
244
+ width: 150px;
245
+ }
246
+
247
+ footer {
248
+ clear: both;
249
+ }
250
+
251
+ pre.terminal {
252
+ border: 1px solid black;
253
+ background-color: #333;
254
+ color: white;
255
+ padding: 5px;
256
+ overflow: auto;
257
+ word-wrap: break-word;
258
+ margin: 0px;
259
+ }
260
+
261
+ pre.terminal code {
262
+ font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
263
+ background-color: #333;
264
+ }
@@ -0,0 +1,55 @@
1
+ var client;
2
+ var subscription;
3
+
4
+ function handleMessage(message){
5
+ var project = message.project;
6
+ var status = message.status;
7
+ var last_build = message.last_build;
8
+ $('#'+ project + ' .status').removeClass('running passed failed').addClass(status);
9
+ if(typeof(last_build) != 'undefined') {
10
+ $('.last_build').html("Latest Build: " + last_build);
11
+ }
12
+ }
13
+
14
+ function initializeFaye(channel){
15
+ var ch = (typeof(channel) == 'undefined') ? '/all' : channel;
16
+ client = new Faye.Client('/faye');
17
+ var subscription = client.subscribe(ch, function(message){
18
+ handleMessage(message);
19
+ });
20
+ }
21
+
22
+ function setupBuildToggles(){
23
+ $('.results_toggle').click(function(){
24
+ $(this).siblings('.results').slideToggle();
25
+ });
26
+ }
27
+
28
+
29
+ function setupBuildButtons(){
30
+ $('.build_all').click(function(){
31
+ $.post('/command/build_all');
32
+ });
33
+
34
+ $('.build_project').click(function(){
35
+ var project = $(this).closest('.project').attr('id');
36
+ if(typeof(project) != 'undefined'){
37
+ $.post('/' + project + '/build');
38
+ }
39
+ });
40
+ }
41
+
42
+ function linkProjectCells(){
43
+ $('.project').click(function(){
44
+ var project = $(this).attr('id');
45
+ if(typeof(project) != 'undefined'){
46
+ window.location = "/" + project;
47
+ }
48
+ });
49
+ }
50
+
51
+ $(function(){
52
+ initializeFaye();
53
+ setupBuildToggles(); //if we're on an individual project page.
54
+ setupBuildButtons();
55
+ });
@@ -0,0 +1,61 @@
1
+ require 'sinatra/base'
2
+ require 'faye'
3
+ require 'erb'
4
+
5
+ module VetCI
6
+ class Server < Sinatra::Base
7
+
8
+ lib_dir = File.dirname(File.expand_path(__FILE__))
9
+
10
+ set :views, "#{lib_dir}/views"
11
+ set :public, "#{lib_dir}/public"
12
+ set :static, true
13
+
14
+ use Faye::RackAdapter, :mount => '/faye',
15
+ :timeout => 25
16
+
17
+ helpers do
18
+ # Thanks CI-Joe, and Integrity by association.
19
+ def ansi_color_codes(string)
20
+ string.gsub("\e[0m", '</span>').
21
+ gsub(/\e\[(\d+)m/, "<span class=\"color\\1\">")
22
+ end
23
+ end
24
+
25
+ # Triggers that a project should be built
26
+ post '/:project/build' do
27
+ project = Project.named(params[:project])
28
+ project.build(env['faye.client'], params[:payload]) unless project.nil?
29
+ status 200
30
+ end
31
+
32
+ # DELETES a project's build
33
+ #get '/:project/builds/:build_id/destroy' do
34
+ # redirect "/#{params[:project]}"
35
+ #end
36
+
37
+ #Renders the project's details page.
38
+ get '/:project' do
39
+ @project = Project.named(params[:project])
40
+ pass if @project.nil?
41
+ @builds = @project.pagedBuilds(params[:page], 5)
42
+ erb :project
43
+ end
44
+
45
+ post '/command/build_all' do
46
+ Project.projects.each do |name, project|
47
+ project.build(env['faye.client'])
48
+ end
49
+ status 200
50
+ end
51
+
52
+ get '/' do
53
+ @projects = Project.projects
54
+ erb :index
55
+ end
56
+
57
+ def self.start
58
+ VetCI::Server.run! :host => '0.0.0.0', :port => 4567
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,31 @@
1
+ # Our setup script.
2
+
3
+ module VetCI
4
+ class Setup
5
+ class << self
6
+ def go
7
+ unless Dir.exists? '.vetci'
8
+ Dir.mkdir '.vetci'
9
+ end
10
+
11
+ unless File.exists? 'Vetfile'
12
+ puts "No Vetfile found. Please create one and try again."
13
+ Process.exit
14
+ end
15
+
16
+ begin
17
+ parameters = YAML.load(File.read('Vetfile'))
18
+ rescue
19
+ puts "Unable to load Vetfile. Check its syntax and try again."
20
+ Process.exit
21
+ end
22
+
23
+ Project.datastore = PStore.new(File.join('.vetci', 'projects.pstore'))
24
+
25
+ Project.parse_vetfile_contents(parameters["projects"])
26
+
27
+ return parameters
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ module VetCI
2
+ class Util
3
+ class << self
4
+ def open_pipe(command)
5
+ read, write = IO.pipe
6
+ process_id = fork do
7
+ $stdout.reopen write
8
+ exec command
9
+ end
10
+ write.close
11
+ yield read, process_id
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ module Vet
2
+ module Ci
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ <% @projects.each do |name, project| %>
2
+ <div class="project" id="<%= project.name %>">
3
+ <h2><%= project.name %></h2>
4
+ <span class="status <%= project.last_build_status %>"></span>
5
+ <div class="bottom_tab">
6
+ <span class="last_build">Latest Build: <%= project.builds.first.dashboard_time unless project.builds.first.nil? %></span>
7
+ <a class="tab_button view_project" href="/<%= project.name %>">Details</a>
8
+ <a class="tab_button build_project" href="#">Build</a>
9
+ </div>
10
+ </div>
11
+ <% end %>
@@ -0,0 +1,22 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>VetCI</title>
5
+ <link rel="stylesheet" href="/css/style.css" type="text/css" charset="utf-8">
6
+ </head>
7
+ <body>
8
+ <header>
9
+ <h1 class="logo"><a href="/">VetCI</a></h1>
10
+ <button class="build_all">Build All</button>
11
+ </header>
12
+ <section id="main">
13
+ <%= yield %>
14
+ </section>
15
+ <footer>
16
+
17
+ </footer>
18
+ <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" type="text/javascript" charset="utf-8"></script>
19
+ <script src="/faye.js" type="text/javascript" charset="utf-8"></script>
20
+ <script src="/js/application.js" type="text/javascript" charset="utf-8"></script>
21
+ </body>
22
+ </html>
@@ -0,0 +1,38 @@
1
+ <div class="project" id="<%= @project.name %>">
2
+ <h2>
3
+ <%= @project.name %>
4
+ </h2>
5
+ <span class="status <%= @project.last_build_status %>"></span>
6
+ <div class="bottom_tab">
7
+ <span class="last_build">Latest Build: <%= @project.builds.first.dashboard_time unless @project.builds.first.nil? %></span>
8
+ <a class="tab_button build_project" href="#">Build</a>
9
+ </div>
10
+ </div>
11
+
12
+ <% @builds.each do |build| %>
13
+ <div class="build">
14
+ <span class="status <%= build.status_class %>"></span>
15
+ <p class="title">
16
+ Build Time: <%= build.friendly_time %>
17
+ </p>
18
+
19
+ <div class="build_details">
20
+ <div><span class="label">Committer:</span><%= build.commit_info["committer"]["name"]%></div>
21
+ <div><span class="label">Comment:</span><%= build.commit_info["message"]%></div>
22
+ </div>
23
+
24
+ <% unless build.payload.nil? %>
25
+ <div class="build_details">
26
+ <div><span class="label">Github Project:</span><%= build.payload["repository"]["name"] %></div>
27
+ <div><span class="label">Compare URL:</span><%= build.payload["compare"]%></div>
28
+ </div>
29
+ <% end %>
30
+
31
+ <div class="results">
32
+ <pre class="terminal"><code><%=ansi_color_codes escape_html(build.output) %></code></pre>
33
+ </div>
34
+ <p class="results_toggle">
35
+ show more
36
+ </p>
37
+ </div>
38
+ <% end %>
@@ -0,0 +1,11 @@
1
+ require './lib/vet-ci'
2
+
3
+ #VetCI::Setup.prepare_database
4
+
5
+ # Testing files should actually go here
6
+
7
+ describe VetCI::Project do
8
+ it 'creates a new Project' do
9
+
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "vet-ci/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "vet-ci"
7
+ s.version = Vet::Ci::VERSION
8
+ s.authors = ["Lee Adkins"]
9
+ s.email = ["lee@ravsonic.com"]
10
+ s.homepage = "http://github.com/leeadkins/vet-ci"
11
+ s.summary = %q{Make sure your little code pets get their checkups.}
12
+ s.description = %q{VetCI is a continuous intergration system. It's a work in progress, so documentation is slim right now. You'll probably want to wait until at least 0.0.5 to actually use it.}
13
+
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ #s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.executables = %w( vet-ci )
19
+ s.require_paths = ["lib"]
20
+
21
+
22
+
23
+ s.add_runtime_dependency 'sinatra'
24
+ s.add_runtime_dependency 'grit'
25
+ s.add_runtime_dependency 'choice'
26
+ s.add_runtime_dependency 'faye'
27
+
28
+ s.add_development_dependency 'rake'
29
+ s.add_development_dependency 'rspec'
30
+ end
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vet-ci
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Lee Adkins
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-07-26 00:00:00.000000000 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: sinatra
17
+ requirement: &2152757940 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *2152757940
26
+ - !ruby/object:Gem::Dependency
27
+ name: grit
28
+ requirement: &2152757300 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *2152757300
37
+ - !ruby/object:Gem::Dependency
38
+ name: choice
39
+ requirement: &2152756680 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: *2152756680
48
+ - !ruby/object:Gem::Dependency
49
+ name: faye
50
+ requirement: &2152755980 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :runtime
57
+ prerelease: false
58
+ version_requirements: *2152755980
59
+ - !ruby/object:Gem::Dependency
60
+ name: rake
61
+ requirement: &2152755560 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: *2152755560
70
+ - !ruby/object:Gem::Dependency
71
+ name: rspec
72
+ requirement: &2152755140 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: *2152755140
81
+ description: VetCI is a continuous intergration system. It's a work in progress, so
82
+ documentation is slim right now. You'll probably want to wait until at least 0.0.5
83
+ to actually use it.
84
+ email:
85
+ - lee@ravsonic.com
86
+ executables:
87
+ - vet-ci
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - .gitignore
92
+ - Gemfile
93
+ - README.md
94
+ - Rakefile
95
+ - Vetfile
96
+ - Vetfile.example
97
+ - bin/vet-ci
98
+ - lib/vet-ci.rb
99
+ - lib/vet-ci/build.rb
100
+ - lib/vet-ci/project.rb
101
+ - lib/vet-ci/public/css/style.css
102
+ - lib/vet-ci/public/js/application.js
103
+ - lib/vet-ci/server.rb
104
+ - lib/vet-ci/setup.rb
105
+ - lib/vet-ci/util.rb
106
+ - lib/vet-ci/version.rb
107
+ - lib/vet-ci/views/index.erb
108
+ - lib/vet-ci/views/layout.erb
109
+ - lib/vet-ci/views/project.erb
110
+ - spec/vet_spec.rb
111
+ - vet-ci.gemspec
112
+ has_rdoc: true
113
+ homepage: http://github.com/leeadkins/vet-ci
114
+ licenses: []
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ segments:
126
+ - 0
127
+ hash: 4042315856108701698
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ segments:
135
+ - 0
136
+ hash: 4042315856108701698
137
+ requirements: []
138
+ rubyforge_project:
139
+ rubygems_version: 1.6.2
140
+ signing_key:
141
+ specification_version: 3
142
+ summary: Make sure your little code pets get their checkups.
143
+ test_files:
144
+ - spec/vet_spec.rb