janky 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|