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.
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