janky 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +3 -0
- data/COPYING +22 -0
- data/Gemfile +2 -0
- data/README.md +211 -0
- data/Rakefile +19 -0
- data/config.ru +3 -0
- data/janky.gemspec +102 -0
- data/lib/janky.rb +224 -0
- data/lib/janky/app.rb +81 -0
- data/lib/janky/branch.rb +112 -0
- data/lib/janky/build.rb +223 -0
- data/lib/janky/build_request.rb +49 -0
- data/lib/janky/builder.rb +108 -0
- data/lib/janky/builder/client.rb +82 -0
- data/lib/janky/builder/http.rb +43 -0
- data/lib/janky/builder/mock.rb +45 -0
- data/lib/janky/builder/payload.rb +63 -0
- data/lib/janky/builder/receiver.rb +20 -0
- data/lib/janky/builder/runner.rb +47 -0
- data/lib/janky/campfire.rb +127 -0
- data/lib/janky/campfire/mock.rb +0 -0
- data/lib/janky/commit.rb +14 -0
- data/lib/janky/database/migrate/1312115512_init.rb +48 -0
- data/lib/janky/database/migrate/1312117285_non_unique_repo_uri.rb +10 -0
- data/lib/janky/database/migrate/1312198807_repo_enabled.rb +11 -0
- data/lib/janky/database/migrate/1313867551_add_build_output_column.rb +9 -0
- data/lib/janky/database/migrate/1313871652_add_commit_url_column.rb +9 -0
- data/lib/janky/database/migrate/1317384618_add_repo_hook_url.rb +9 -0
- data/lib/janky/database/migrate/1317384619_add_build_room_id.rb +9 -0
- data/lib/janky/database/migrate/1317384629_drop_default_room_id.rb +9 -0
- data/lib/janky/database/migrate/1317384649_github_team_id.rb +9 -0
- data/lib/janky/database/schema.rb +68 -0
- data/lib/janky/database/seed.dump.gz +0 -0
- data/lib/janky/exception.rb +62 -0
- data/lib/janky/github.rb +67 -0
- data/lib/janky/github/api.rb +69 -0
- data/lib/janky/github/commit.rb +27 -0
- data/lib/janky/github/mock.rb +47 -0
- data/lib/janky/github/payload.rb +34 -0
- data/lib/janky/github/payload_parser.rb +57 -0
- data/lib/janky/github/receiver.rb +69 -0
- data/lib/janky/helpers.rb +17 -0
- data/lib/janky/hubot.rb +117 -0
- data/lib/janky/job_creator.rb +111 -0
- data/lib/janky/notifier.rb +84 -0
- data/lib/janky/notifier/campfire.rb +21 -0
- data/lib/janky/notifier/mock.rb +55 -0
- data/lib/janky/notifier/multi.rb +22 -0
- data/lib/janky/public/css/base.css +204 -0
- data/lib/janky/public/images/building-bot.gif +0 -0
- data/lib/janky/public/images/disclosure-arrow.png +0 -0
- data/lib/janky/public/images/logo.png +0 -0
- data/lib/janky/public/images/robawt-status.gif +0 -0
- data/lib/janky/public/javascripts/application.js +3 -0
- data/lib/janky/public/javascripts/jquery.js +16 -0
- data/lib/janky/public/javascripts/jquery.relatize.js +111 -0
- data/lib/janky/repository.rb +174 -0
- data/lib/janky/tasks.rb +36 -0
- data/lib/janky/templates/console.mustache +4 -0
- data/lib/janky/templates/index.mustache +11 -0
- data/lib/janky/templates/layout.mustache +22 -0
- data/lib/janky/version.rb +3 -0
- data/lib/janky/views/console.rb +33 -0
- data/lib/janky/views/index.rb +35 -0
- data/lib/janky/views/layout.rb +19 -0
- data/test/default.xml.erb +0 -0
- data/test/janky_test.rb +271 -0
- data/test/test_helper.rb +107 -0
- metadata +319 -0
data/lib/janky/app.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
module Janky
|
2
|
+
class App < Sinatra::Base
|
3
|
+
register Mustache::Sinatra
|
4
|
+
register Helpers
|
5
|
+
|
6
|
+
set :app_file, __FILE__
|
7
|
+
enable :static
|
8
|
+
|
9
|
+
set :mustache, {
|
10
|
+
:namespace => Janky,
|
11
|
+
:views => File.join(root, "views"),
|
12
|
+
:templates => File.join(root, "templates")
|
13
|
+
}
|
14
|
+
|
15
|
+
before do
|
16
|
+
if organization = github_organization
|
17
|
+
github_organization_authenticate!(organization)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def github_organization
|
22
|
+
settings.respond_to?(:github_organization) && settings.github_organization
|
23
|
+
end
|
24
|
+
|
25
|
+
def github_team_id
|
26
|
+
settings.respond_to?(:github_team_id) && settings.github_team_id
|
27
|
+
end
|
28
|
+
|
29
|
+
def authorize_index
|
30
|
+
if github_team_id
|
31
|
+
github_team_authenticate!(github_team_id)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def authorize_repo(repo)
|
36
|
+
if team_id = (repo.github_team_id || github_team_id)
|
37
|
+
github_team_authenticate!(team_id)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
get "/?" do
|
42
|
+
authorize_index
|
43
|
+
@builds = Build.started.first(50)
|
44
|
+
mustache :index
|
45
|
+
end
|
46
|
+
|
47
|
+
get "/:build_id/output" do |build_id|
|
48
|
+
@build = Build.find(build_id)
|
49
|
+
authorize_repo(@build.repo)
|
50
|
+
mustache :console, :layout => false
|
51
|
+
end
|
52
|
+
|
53
|
+
get "/:repo_name" do |repo_name|
|
54
|
+
repo = find_repo(repo_name)
|
55
|
+
authorize_repo(repo)
|
56
|
+
|
57
|
+
@builds = repo.builds.started.first(50)
|
58
|
+
mustache :index
|
59
|
+
end
|
60
|
+
|
61
|
+
get "/:repo_name/:branch" do |repo_name, branch|
|
62
|
+
repo = find_repo(repo_name)
|
63
|
+
authorize_repo(repo)
|
64
|
+
|
65
|
+
@builds = repo.branch_for(branch).builds.started.first(50)
|
66
|
+
mustache :index
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class NoAuth < Sinatra::Base
|
71
|
+
register Helpers
|
72
|
+
|
73
|
+
get "/boomtown" do
|
74
|
+
raise Error, "boomtown"
|
75
|
+
end
|
76
|
+
|
77
|
+
get "/site/sha" do
|
78
|
+
`cd /data/janky && git rev-parse HEAD`
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/janky/branch.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
module Janky
|
2
|
+
class Branch < ActiveRecord::Base
|
3
|
+
belongs_to :repository
|
4
|
+
has_many :builds
|
5
|
+
|
6
|
+
# Is this branch green?
|
7
|
+
#
|
8
|
+
# Returns a Boolean.
|
9
|
+
def green?
|
10
|
+
if current_build
|
11
|
+
current_build.green?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Is this branch red?
|
16
|
+
#
|
17
|
+
# Returns a Boolean.
|
18
|
+
def red?
|
19
|
+
if current_build
|
20
|
+
current_build.red?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Is this branch building?
|
25
|
+
#
|
26
|
+
# Returns a Boolean.
|
27
|
+
def building?
|
28
|
+
if current_build
|
29
|
+
current_build.building?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Is this branch completed?
|
34
|
+
#
|
35
|
+
# Returns a Boolean.
|
36
|
+
def completed?
|
37
|
+
if current_build
|
38
|
+
current_build.completed?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Find all completed builds, sorted by completion date, most recent first.
|
43
|
+
#
|
44
|
+
# Returns an Array of Builds.
|
45
|
+
def completed_builds
|
46
|
+
builds.completed
|
47
|
+
end
|
48
|
+
|
49
|
+
# Create a build for the given commit.
|
50
|
+
#
|
51
|
+
# commit - the Janky::Commit instance to build.
|
52
|
+
# compare - optional String GitHub Compare View URL. Defaults to the
|
53
|
+
# commit last build, if any.
|
54
|
+
# room_id - optional Fixnum Campfire room ID. Defaults to the room set on
|
55
|
+
# the repository.
|
56
|
+
#
|
57
|
+
# Returns the newly created Janky::Build.
|
58
|
+
def build_for(commit, room_id = nil, compare = nil)
|
59
|
+
if compare.nil? && build = commit.last_build
|
60
|
+
compare = build.compare
|
61
|
+
end
|
62
|
+
|
63
|
+
if room_id.nil? || room_id.zero?
|
64
|
+
room_id = repository.room_id
|
65
|
+
end
|
66
|
+
|
67
|
+
builds.create(
|
68
|
+
:compare => compare,
|
69
|
+
:commit => commit,
|
70
|
+
:room_id => room_id
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
# The current build, e.g. the most recent one.
|
75
|
+
#
|
76
|
+
# Returns a Build.
|
77
|
+
def current_build
|
78
|
+
builds.last
|
79
|
+
end
|
80
|
+
|
81
|
+
# Human readable status of this branch
|
82
|
+
#
|
83
|
+
# Returns a String.
|
84
|
+
def status
|
85
|
+
if current_build && current_build.building?
|
86
|
+
"building"
|
87
|
+
elsif build = completed_builds.first
|
88
|
+
if build.green?
|
89
|
+
"green"
|
90
|
+
elsif build.red?
|
91
|
+
"red"
|
92
|
+
end
|
93
|
+
elsif completed_builds.empty? || builds.empty?
|
94
|
+
"no build"
|
95
|
+
else
|
96
|
+
raise Error, "unexpected branch status: #{id.inspect}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Hash representation of this branch status.
|
101
|
+
#
|
102
|
+
# Returns a Hash with the name, status, sha1 and compare url.
|
103
|
+
def to_hash
|
104
|
+
{
|
105
|
+
:name => repository.name,
|
106
|
+
:status => status,
|
107
|
+
:sha1 => (current_build && current_build.sha1),
|
108
|
+
:compare => (current_build && current_build.compare)
|
109
|
+
}
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
data/lib/janky/build.rb
ADDED
@@ -0,0 +1,223 @@
|
|
1
|
+
module Janky
|
2
|
+
class Build < ActiveRecord::Base
|
3
|
+
belongs_to :branch
|
4
|
+
belongs_to :commit
|
5
|
+
|
6
|
+
# Transition the Build to the started state.
|
7
|
+
#
|
8
|
+
# id - the Fixnum ID used to find the build.
|
9
|
+
# url - the full String URL of the build.
|
10
|
+
#
|
11
|
+
# Returns nothing or raises an Error for inexistant builds.
|
12
|
+
def self.start(id, url)
|
13
|
+
if build = find_by_id(id)
|
14
|
+
build.start(url, Time.now)
|
15
|
+
else
|
16
|
+
raise Error, "Unknown build: #{id.inspect}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Transition the Build to the completed state.
|
21
|
+
#
|
22
|
+
# id - the Fixnum ID used to find the build.
|
23
|
+
# green - Boolean indicating build success.
|
24
|
+
#
|
25
|
+
# Returns nothing or raises an Error for inexistant builds.
|
26
|
+
def self.complete(id, green)
|
27
|
+
if build = find_by_id(id)
|
28
|
+
build.complete(green, Time.now)
|
29
|
+
else
|
30
|
+
raise Error, "Unknown build: #{id.inspect}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Find all started builds, most recent first.
|
35
|
+
#
|
36
|
+
# Returns an Array of Builds.
|
37
|
+
def self.started
|
38
|
+
where("started_at IS NOT NULL").order("started_at DESC")
|
39
|
+
end
|
40
|
+
|
41
|
+
# Find all completed builds, most recent first.
|
42
|
+
#
|
43
|
+
# Returns an Array of Builds.
|
44
|
+
def self.completed
|
45
|
+
started.
|
46
|
+
where("completed_at IS NOT NULL")
|
47
|
+
end
|
48
|
+
|
49
|
+
# Find all green builds, most recent first.
|
50
|
+
#
|
51
|
+
# Returns an Array of Builds.
|
52
|
+
def self.green
|
53
|
+
completed.where(:green => true)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Is this build currently being built?
|
57
|
+
#
|
58
|
+
# Returns a Boolean.
|
59
|
+
def building?
|
60
|
+
started? && !completed?
|
61
|
+
end
|
62
|
+
|
63
|
+
# Is this build red?
|
64
|
+
#
|
65
|
+
# Returns a Boolean, nothing when the build hasn't completed yet.
|
66
|
+
def red?
|
67
|
+
completed? && !green?
|
68
|
+
end
|
69
|
+
|
70
|
+
# Was this build ever started?
|
71
|
+
#
|
72
|
+
# Returns a Boolean.
|
73
|
+
def started?
|
74
|
+
! started_at.nil?
|
75
|
+
end
|
76
|
+
|
77
|
+
# Did this build complete?
|
78
|
+
#
|
79
|
+
# Returns a Boolean.
|
80
|
+
def completed?
|
81
|
+
! completed_at.nil?
|
82
|
+
end
|
83
|
+
|
84
|
+
# Trigger a Jenkins build using the appropriate builder.
|
85
|
+
#
|
86
|
+
# Returns nothing.
|
87
|
+
def run
|
88
|
+
builder.run(self)
|
89
|
+
end
|
90
|
+
|
91
|
+
# See Repository#builder.
|
92
|
+
def builder
|
93
|
+
branch.repository.builder
|
94
|
+
end
|
95
|
+
|
96
|
+
# Run a copy of itself. Typically used to force a build in case of
|
97
|
+
# temporary test failure or when auto-build is disabled.
|
98
|
+
#
|
99
|
+
# new_room_id - optional Campfire room Fixnum ID. Defaults to the room of the
|
100
|
+
# build being re-run.
|
101
|
+
#
|
102
|
+
# Returns the build copy.
|
103
|
+
def rerun(new_room_id = nil)
|
104
|
+
build = branch.build_for(commit, new_room_id)
|
105
|
+
build.run
|
106
|
+
build
|
107
|
+
end
|
108
|
+
|
109
|
+
# Cached or remote build output.
|
110
|
+
#
|
111
|
+
# Returns the String output.
|
112
|
+
def output
|
113
|
+
if completed?
|
114
|
+
read_attribute(:output)
|
115
|
+
elsif started?
|
116
|
+
output_remote
|
117
|
+
else
|
118
|
+
""
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Retrieve the build output from the Jenkins server.
|
123
|
+
#
|
124
|
+
# Returns the String output.
|
125
|
+
def output_remote
|
126
|
+
if started?
|
127
|
+
builder.output(self)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Mark the build as started.
|
132
|
+
#
|
133
|
+
# url - the full String URL of the build on the Jenkins server.
|
134
|
+
# now - the Time at which the build started.
|
135
|
+
#
|
136
|
+
# Returns nothing or raise an Error for weird transitions.
|
137
|
+
def start(url, now)
|
138
|
+
if started?
|
139
|
+
raise Error, "Build #{id} already started"
|
140
|
+
elsif completed?
|
141
|
+
raise Error, "Build #{id} already completed"
|
142
|
+
else
|
143
|
+
update_attributes!(:url => url, :started_at => now)
|
144
|
+
Notifier.started(self)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Mark the build as complete, store the build output and notify Campfire.
|
149
|
+
#
|
150
|
+
# green - Boolean indicating build success.
|
151
|
+
# now - the Time at which the build completed.
|
152
|
+
#
|
153
|
+
# Returns nothing or raise an Error for weird transitions.
|
154
|
+
def complete(green, now)
|
155
|
+
if ! started?
|
156
|
+
raise Error, "Build #{id} not started"
|
157
|
+
elsif completed?
|
158
|
+
raise Error, "Build #{id} already completed"
|
159
|
+
else
|
160
|
+
update_attributes!(
|
161
|
+
:green => green,
|
162
|
+
:completed_at => now,
|
163
|
+
:output => output_remote
|
164
|
+
)
|
165
|
+
Notifier.completed(self)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# The time it took to peform this build in seconds.
|
170
|
+
#
|
171
|
+
# Returns an Integer seconds.
|
172
|
+
def duration
|
173
|
+
if completed?
|
174
|
+
Integer(completed_at - started_at)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# The name of the Campfire room where notifications are sent.
|
179
|
+
#
|
180
|
+
# Returns the String room name.
|
181
|
+
def room_name
|
182
|
+
if room_id && room_id > 0
|
183
|
+
Campfire.room_name(room_id)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def repo_id
|
188
|
+
repository.id
|
189
|
+
end
|
190
|
+
|
191
|
+
def repo_job_name
|
192
|
+
repository.job_name
|
193
|
+
end
|
194
|
+
|
195
|
+
def repo_name
|
196
|
+
repository.name
|
197
|
+
end
|
198
|
+
|
199
|
+
def repository
|
200
|
+
branch.repository
|
201
|
+
end
|
202
|
+
|
203
|
+
def repo
|
204
|
+
branch.repository
|
205
|
+
end
|
206
|
+
|
207
|
+
def sha1
|
208
|
+
commit.short_sha
|
209
|
+
end
|
210
|
+
|
211
|
+
def commit_url
|
212
|
+
commit.url
|
213
|
+
end
|
214
|
+
|
215
|
+
def number
|
216
|
+
id.to_s
|
217
|
+
end
|
218
|
+
|
219
|
+
def branch_name
|
220
|
+
branch.name
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Janky
|
2
|
+
class BuildRequest
|
3
|
+
def self.handle(repo_uri, branch_name, commit, compare, room_id)
|
4
|
+
repos = Repository.find_all_by_uri(repo_uri)
|
5
|
+
repos.each do |repo|
|
6
|
+
begin
|
7
|
+
new(repo, branch_name, commit, compare, room_id).handle
|
8
|
+
rescue Janky::Error => boom
|
9
|
+
Exception.report(boom, :repo => repo.name)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
repos.size
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(repo, branch_name, commit, compare, room_id)
|
17
|
+
@repo = repo
|
18
|
+
@branch_name = branch_name
|
19
|
+
@commit = commit
|
20
|
+
@compare = compare
|
21
|
+
@room_id = room_id
|
22
|
+
end
|
23
|
+
|
24
|
+
def handle
|
25
|
+
current_build = commit.last_build
|
26
|
+
build = branch.build_for(commit, @room_id, @compare)
|
27
|
+
|
28
|
+
if !current_build || (current_build && current_build.red?)
|
29
|
+
if @repo.enabled?
|
30
|
+
build.run
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def branch
|
36
|
+
@repo.branch_for(@branch_name)
|
37
|
+
end
|
38
|
+
|
39
|
+
def commit
|
40
|
+
@repo.commit_for(
|
41
|
+
:sha1 => @commit.sha1,
|
42
|
+
:url => @commit.url,
|
43
|
+
:message => @commit.message,
|
44
|
+
:author => @commit.author,
|
45
|
+
:committed_at => @commit.committed_at
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|