janky 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/CHANGES +3 -0
  2. data/COPYING +22 -0
  3. data/Gemfile +2 -0
  4. data/README.md +211 -0
  5. data/Rakefile +19 -0
  6. data/config.ru +3 -0
  7. data/janky.gemspec +102 -0
  8. data/lib/janky.rb +224 -0
  9. data/lib/janky/app.rb +81 -0
  10. data/lib/janky/branch.rb +112 -0
  11. data/lib/janky/build.rb +223 -0
  12. data/lib/janky/build_request.rb +49 -0
  13. data/lib/janky/builder.rb +108 -0
  14. data/lib/janky/builder/client.rb +82 -0
  15. data/lib/janky/builder/http.rb +43 -0
  16. data/lib/janky/builder/mock.rb +45 -0
  17. data/lib/janky/builder/payload.rb +63 -0
  18. data/lib/janky/builder/receiver.rb +20 -0
  19. data/lib/janky/builder/runner.rb +47 -0
  20. data/lib/janky/campfire.rb +127 -0
  21. data/lib/janky/campfire/mock.rb +0 -0
  22. data/lib/janky/commit.rb +14 -0
  23. data/lib/janky/database/migrate/1312115512_init.rb +48 -0
  24. data/lib/janky/database/migrate/1312117285_non_unique_repo_uri.rb +10 -0
  25. data/lib/janky/database/migrate/1312198807_repo_enabled.rb +11 -0
  26. data/lib/janky/database/migrate/1313867551_add_build_output_column.rb +9 -0
  27. data/lib/janky/database/migrate/1313871652_add_commit_url_column.rb +9 -0
  28. data/lib/janky/database/migrate/1317384618_add_repo_hook_url.rb +9 -0
  29. data/lib/janky/database/migrate/1317384619_add_build_room_id.rb +9 -0
  30. data/lib/janky/database/migrate/1317384629_drop_default_room_id.rb +9 -0
  31. data/lib/janky/database/migrate/1317384649_github_team_id.rb +9 -0
  32. data/lib/janky/database/schema.rb +68 -0
  33. data/lib/janky/database/seed.dump.gz +0 -0
  34. data/lib/janky/exception.rb +62 -0
  35. data/lib/janky/github.rb +67 -0
  36. data/lib/janky/github/api.rb +69 -0
  37. data/lib/janky/github/commit.rb +27 -0
  38. data/lib/janky/github/mock.rb +47 -0
  39. data/lib/janky/github/payload.rb +34 -0
  40. data/lib/janky/github/payload_parser.rb +57 -0
  41. data/lib/janky/github/receiver.rb +69 -0
  42. data/lib/janky/helpers.rb +17 -0
  43. data/lib/janky/hubot.rb +117 -0
  44. data/lib/janky/job_creator.rb +111 -0
  45. data/lib/janky/notifier.rb +84 -0
  46. data/lib/janky/notifier/campfire.rb +21 -0
  47. data/lib/janky/notifier/mock.rb +55 -0
  48. data/lib/janky/notifier/multi.rb +22 -0
  49. data/lib/janky/public/css/base.css +204 -0
  50. data/lib/janky/public/images/building-bot.gif +0 -0
  51. data/lib/janky/public/images/disclosure-arrow.png +0 -0
  52. data/lib/janky/public/images/logo.png +0 -0
  53. data/lib/janky/public/images/robawt-status.gif +0 -0
  54. data/lib/janky/public/javascripts/application.js +3 -0
  55. data/lib/janky/public/javascripts/jquery.js +16 -0
  56. data/lib/janky/public/javascripts/jquery.relatize.js +111 -0
  57. data/lib/janky/repository.rb +174 -0
  58. data/lib/janky/tasks.rb +36 -0
  59. data/lib/janky/templates/console.mustache +4 -0
  60. data/lib/janky/templates/index.mustache +11 -0
  61. data/lib/janky/templates/layout.mustache +22 -0
  62. data/lib/janky/version.rb +3 -0
  63. data/lib/janky/views/console.rb +33 -0
  64. data/lib/janky/views/index.rb +35 -0
  65. data/lib/janky/views/layout.rb +19 -0
  66. data/test/default.xml.erb +0 -0
  67. data/test/janky_test.rb +271 -0
  68. data/test/test_helper.rb +107 -0
  69. 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
@@ -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
@@ -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