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
@@ -0,0 +1,174 @@
1
+ module Janky
2
+ class Repository < ActiveRecord::Base
3
+ has_many :branches
4
+ has_many :commits
5
+ has_many :builds, :through => :branches
6
+
7
+ replicate_associations :builds, :commits, :branches
8
+
9
+ default_scope(order("name"))
10
+
11
+ def self.setup(nwo, name = nil)
12
+ if nwo.nil?
13
+ raise ArgumentError, "nwo can't be nil"
14
+ end
15
+
16
+ if repo = Repository.find_by_name(nwo)
17
+ repo.setup
18
+ return repo
19
+ end
20
+
21
+ repo = GitHub.repo_get(nwo)
22
+ return if !repo
23
+
24
+ uri = repo["private"] ? repo["ssh_url"] : repo["git_url"]
25
+ name ||= repo["name"]
26
+ uri.gsub!(/\.git$/, "")
27
+
28
+ repo =
29
+ if repo = Repository.find_by_name(name)
30
+ repo.update_attributes!(:uri => uri)
31
+ repo
32
+ else
33
+ Repository.create!(:name => name, :uri => uri)
34
+ end
35
+
36
+ repo.setup
37
+ repo
38
+ end
39
+
40
+ # Find a named repository.
41
+ #
42
+ # name - The String name of the repository.
43
+ #
44
+ # Returns a Repository or nil when it doesn't exists.
45
+ def self.by_name(name)
46
+ find_by_name(name)
47
+ end
48
+
49
+ # Toggle auto-build feature of this repo. When enabled (default),
50
+ # all branches are built automatically.
51
+ #
52
+ # Returns the new flag status as a Boolean.
53
+ def toggle_auto_build
54
+ toggle(:enabled)
55
+ save!
56
+ enabled
57
+ end
58
+
59
+ # Create or retrieve the named branch.
60
+ #
61
+ # name - The branch's name as a String.
62
+ #
63
+ # Returns a Branch record.
64
+ def branch_for(name)
65
+ branches.find_or_create_by_name(name)
66
+ end
67
+
68
+ # Create or retrieve the given commit.
69
+ #
70
+ # name - The Hash representation of the Commit.
71
+ #
72
+ # Returns a Commit record.
73
+ def commit_for(commit)
74
+ commits.find_by_sha1(commit[:sha1]) ||
75
+ commits.create(commit)
76
+ end
77
+
78
+ # Jenkins host executing this repo's builds.
79
+ #
80
+ # Returns a Builder::Client.
81
+ def builder
82
+ Builder.pick_for(self)
83
+ end
84
+
85
+ # GitHub user owning this repo.
86
+ #
87
+ # Returns the user name as a String.
88
+ def github_owner
89
+ uri[/github\.com[\/:](\w+)\//] && $1
90
+ end
91
+
92
+ # Name of this repository on GitHub.
93
+ #
94
+ # Returns the name as a String.
95
+ def github_name
96
+ uri[/github\.com[\/:](\w+)\/([a-zA-Z0-9\-_]+)/] && $2
97
+ end
98
+
99
+ # Name of the Campfire room receiving build notifications.
100
+ #
101
+ # Returns the name as a String.
102
+ def campfire_room
103
+ Campfire.room_name(room_id)
104
+ end
105
+
106
+ # Ditto but returns the Fixnum room id. Defaults to the one set
107
+ # in Campfire.setup.
108
+ def room_id
109
+ read_attribute(:room_id) || Campfire.default_room_id
110
+ end
111
+
112
+ # Setups GitHub and Jenkins for build this repository.
113
+ #
114
+ # Returns nothing.
115
+ def setup
116
+ setup_job
117
+ setup_hook
118
+ end
119
+
120
+ # Create a GitHub hook for this Repository and store its URL if needed.
121
+ #
122
+ # Returns nothing.
123
+ def setup_hook
124
+ if !hook_url || !GitHub.hook_exists?(hook_url)
125
+ url = GitHub.hook_create("#{github_owner}/#{github_name}")
126
+ update_attributes!(:hook_url => url)
127
+ end
128
+ end
129
+
130
+ # Creates a job on the Jenkins server for this repository configuration
131
+ # unless one already exists. Can safely be run multiple times.
132
+ #
133
+ # Returns nothing.
134
+ def setup_job
135
+ builder.setup(job_name, uri, job_config_path)
136
+ end
137
+
138
+ # The path of the Jenkins configuration template. Try "<repo-name>.xml.erb"
139
+ # first then fallback to "default.xml.erb" under the root config directory.
140
+ #
141
+ # Returns the template path as a Pathname.
142
+ def job_config_path
143
+ custom = Janky.jobs_config_dir.join("#{name.downcase}.xml.erb")
144
+ default = Janky.jobs_config_dir.join("default.xml.erb")
145
+
146
+ if custom.readable?
147
+ custom
148
+ elsif default.readable?
149
+ default
150
+ else
151
+ raise Error, "no config.xml template for repo #{id.inspect}"
152
+ end
153
+ end
154
+
155
+ # Construct the URL pointing to this Repository's Jenkins job.
156
+ #
157
+ # Returns the String URL.
158
+ def job_url
159
+ builder.url + "job/#{job_name}"
160
+ end
161
+
162
+ # Calculate the name of the Jenkins job.
163
+ #
164
+ # Returns a String hash of this Repository name and uri.
165
+ def job_name
166
+ md5 = Digest::MD5.new
167
+ md5 << name
168
+ md5 << uri
169
+ md5 << job_config_path.read
170
+ md5 << builder.callback_url.to_s
171
+ md5.hexdigest
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,36 @@
1
+ require "rake"
2
+ require "rake/tasklib"
3
+
4
+ module Janky
5
+ module Tasks
6
+ extend Rake::DSL
7
+
8
+ namespace :db do
9
+ desc "Run the migration(s)"
10
+ task :migrate do
11
+ path = db_dir.join("migrate").to_s
12
+ ActiveRecord::Migration.verbose = true
13
+ ActiveRecord::Migrator.migrate(path)
14
+
15
+ Rake::Task["db:schema:dump"].invoke
16
+ end
17
+
18
+ namespace :schema do
19
+ desc "Dump the database schema into a standard Rails schema.rb file"
20
+ task :dump do
21
+ require "active_record/schema_dumper"
22
+
23
+ path = db_dir.join("schema.rb").to_s
24
+
25
+ File.open(path, "w:utf-8") do |fd|
26
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, fd)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ def self.db_dir
33
+ @db_dir ||= Pathname(__FILE__).expand_path.join("../database")
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,4 @@
1
+ <p>
2
+ <a href="{{ repo_path }}">{{ repo_name }}</a>/<a href="{{ branch_path }}">{{ branch_name }}</a>/<a href="{{ commit_url }}">{{ commit_short_sha }}</a>
3
+ </p>
4
+ <pre>{{ output }}</pre>
@@ -0,0 +1,11 @@
1
+ <ul class="builds">
2
+ {{# jobs }}
3
+ <li class="{{ status }}">
4
+ <a href="{{ console_path }}">
5
+ <span class="status"></span>
6
+ <h2>{{ name }}</h2>
7
+ <p>{{{ last_built_text }}}</p>
8
+ </a>
9
+ </li>
10
+ {{/ jobs }}
11
+ </ul>
@@ -0,0 +1,22 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>{{title}}</title>
6
+ <link rel="stylesheet" href="/css/base.css" type="text/css" />
7
+ <script src="/javascripts/jquery.js" type="text/javascript" charset="utf-8"></script>
8
+ <script src="/javascripts/jquery.relatize.js" type="text/javascript" charset="utf-8"></script>
9
+ <script src="/javascripts/application.js" type="text/javascript" charset="utf-8"></script>
10
+ </head>
11
+ <body class="{{page_class}}">
12
+ <div id="wrapper">
13
+ <h2 id="logo"><a href="{{root}}/">Janky Hubot</a></h2>
14
+
15
+ <div class="content">
16
+ <div class="inside">
17
+ {{{yield}}}
18
+ </div>
19
+ </div>
20
+ </div>
21
+ </body>
22
+ </html>
@@ -0,0 +1,3 @@
1
+ module Janky
2
+ VERSION = "0.9.0"
3
+ end
@@ -0,0 +1,33 @@
1
+ module Janky
2
+ module Views
3
+ class Console < Layout
4
+ def repo_name
5
+ @build.repo_name
6
+ end
7
+
8
+ def repo_path
9
+ "#{root}/#{repo_name}"
10
+ end
11
+
12
+ def branch_name
13
+ @build.branch_name
14
+ end
15
+
16
+ def branch_path
17
+ "#{repo_path}/#{branch_name}"
18
+ end
19
+
20
+ def commit_url
21
+ @build.commit_url
22
+ end
23
+
24
+ def commit_short_sha
25
+ @build.sha1
26
+ end
27
+
28
+ def output
29
+ @build.output
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: UTF-8
2
+ module Janky
3
+ module Views
4
+ class Index < Layout
5
+ def jobs
6
+ @builds.collect do |build|
7
+ {
8
+ :console_path => "/#{build.number}/output",
9
+ :name => "#{build.repo_name}/#{build.branch_name}",
10
+ :status => css_status_for(build),
11
+ :last_built_text => last_built_text_for(build)
12
+ }
13
+ end
14
+ end
15
+
16
+ def css_status_for(build)
17
+ if build.green?
18
+ "good"
19
+ elsif build.building?
20
+ "building"
21
+ else
22
+ "janky"
23
+ end
24
+ end
25
+
26
+ def last_built_text_for(build)
27
+ if build.building?
28
+ "Building since <span class='relatize'>#{build.started_at}</span>…"
29
+ elsif build.completed?
30
+ "Built in <span>#{build.duration}</span> seconds"
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,19 @@
1
+ module Janky
2
+ module Views
3
+ class Layout < Mustache
4
+
5
+ def title
6
+ "Janky Hubot"
7
+ end
8
+
9
+ def page_class
10
+ nil
11
+ end
12
+
13
+ def root
14
+ @request.env['SCRIPT_NAME']
15
+ end
16
+
17
+ end
18
+ end
19
+ end
File without changes
@@ -0,0 +1,271 @@
1
+ require File.expand_path("../test_helper", __FILE__)
2
+
3
+ class JankyTest < Test::Unit::TestCase
4
+ def setup
5
+ Janky.setup(environment)
6
+ Janky.enable_mock!
7
+ Janky.reset!
8
+
9
+ DatabaseCleaner.clean_with(:truncation)
10
+
11
+ Janky::Campfire.rooms = {1 => "enterprise", 2 => "builds"}
12
+ Janky::Campfire.default_room_name = "builds"
13
+
14
+ hubot_setup("github/github")
15
+ end
16
+
17
+ test "green build" do
18
+ Janky::Builder.green!
19
+ gh_post_receive("github")
20
+ Janky::Builder.start!
21
+ Janky::Builder.complete!
22
+
23
+ assert Janky::Notifier.success?("github", "master")
24
+ end
25
+
26
+ test "fail build" do
27
+ Janky::Builder.red!
28
+ gh_post_receive("github")
29
+ Janky::Builder.start!
30
+ Janky::Builder.complete!
31
+
32
+ assert Janky::Notifier.failure?("github", "master")
33
+ end
34
+
35
+ test "pending build" do
36
+ Janky::Builder.green!
37
+ gh_post_receive("github")
38
+
39
+ assert Janky::Notifier.empty?
40
+ Janky::Builder.start!
41
+ Janky::Builder.complete!
42
+ assert Janky::Notifier.success?("github", "master")
43
+ end
44
+
45
+ test "builds multiple repo with the same uri" do
46
+ Janky::Builder.green!
47
+ hubot_setup("github/github", "fi")
48
+ gh_post_receive("github")
49
+ Janky::Builder.start!
50
+ Janky::Builder.complete!
51
+
52
+ assert Janky::Notifier.success?("github", "master")
53
+ assert Janky::Notifier.success?("fi", "master")
54
+ end
55
+
56
+ test "notifies room that triggered the build" do
57
+ Janky::Builder.green!
58
+ gh_post_receive("github")
59
+ Janky::Builder.start!
60
+ Janky::Builder.complete!
61
+
62
+ assert Janky::Notifier.success?("github", "master", "builds")
63
+
64
+ hubot_build("github", "master", "enterprise")
65
+ Janky::Builder.start!
66
+ Janky::Builder.complete!
67
+
68
+ assert Janky::Notifier.success?("github", "master", "enterprise")
69
+ end
70
+
71
+ test "dup commit same branch" do
72
+ Janky::Builder.green!
73
+ gh_post_receive("github", "master", "sha1")
74
+ Janky::Builder.start!
75
+ Janky::Builder.complete!
76
+
77
+ assert Janky::Notifier.notifications.shift
78
+
79
+ gh_post_receive("github", "master", "sha1")
80
+ Janky::Builder.start!
81
+ Janky::Builder.complete!
82
+
83
+ assert Janky::Notifier.notifications.empty?
84
+ end
85
+
86
+ test "dup commit different branch" do
87
+ Janky::Builder.green!
88
+ gh_post_receive("github", "master", "sha1")
89
+ Janky::Builder.start!
90
+ Janky::Builder.complete!
91
+
92
+ assert Janky::Notifier.notifications.shift
93
+
94
+ gh_post_receive("github", "issues-dashboard", "sha1")
95
+ Janky::Builder.start!
96
+ Janky::Builder.complete!
97
+
98
+ assert Janky::Notifier.notifications.empty?
99
+ end
100
+
101
+ test "dup commit currently building" do
102
+ Janky::Builder.green!
103
+ gh_post_receive("github", "master", "sha1")
104
+ Janky::Builder.start!
105
+
106
+ gh_post_receive("github", "issues-dashboard", "sha1")
107
+
108
+ Janky::Builder.complete!
109
+
110
+ assert_equal 1, Janky::Notifier.notifications.size
111
+ assert Janky::Notifier.success?("github", "master")
112
+ end
113
+
114
+ test "dup commit currently red" do
115
+ Janky::Builder.red!
116
+ gh_post_receive("github", "master", "sha1")
117
+ Janky::Builder.start!
118
+ Janky::Builder.complete!
119
+
120
+ assert Janky::Notifier.notifications.shift
121
+
122
+ gh_post_receive("github", "master", "sha1")
123
+
124
+ assert Janky::Notifier.notifications.empty?
125
+ end
126
+
127
+ test "dup commit disabled repo" do
128
+ hubot_setup("github/github", "fi")
129
+ hubot_toggle("fi")
130
+ gh_post_receive("github", "master")
131
+ Janky::Builder.start!
132
+ Janky::Builder.complete!
133
+ Janky::Notifier.reset!
134
+
135
+ hubot_build("fi", "master")
136
+ Janky::Builder.start!
137
+ Janky::Builder.complete!
138
+ assert Janky::Notifier.success?("fi", "master")
139
+ end
140
+
141
+ test "web dashboard" do
142
+ assert get("/").ok?
143
+ assert get("/janky").not_found?
144
+
145
+ gh_post_receive("github")
146
+ assert get("/").ok?
147
+ assert get("/github").ok?
148
+
149
+ Janky::Builder.start!
150
+ assert get("/").ok?
151
+
152
+ Janky::Builder.complete!
153
+ assert get("/").ok?
154
+ assert get("/github").ok?
155
+
156
+ assert get("/github/master").ok?
157
+ assert get("/github/strato").ok?
158
+
159
+ assert get("#{Janky::Build.last.id}/output").ok?
160
+ end
161
+
162
+ test "hubot setup" do
163
+ Janky::GitHub.repo_make_private("github/github")
164
+ assert hubot_setup("github/github").body.
165
+ include?("git@github.com:github/github")
166
+
167
+ Janky::GitHub.repo_make_public("github/github")
168
+ assert hubot_setup("github/github").body.
169
+ include?("git://github.com/github/github")
170
+
171
+ assert_equal 1, hubot_status.body.split("\n").size
172
+
173
+ hubot_setup("github/janky")
174
+ assert_equal 2, hubot_status.body.split("\n").size
175
+
176
+ Janky::GitHub.repo_make_unauthorized("github/enterprise")
177
+ assert hubot_setup("github/enterprise").body.
178
+ include?("Couldn't access github/enterprise")
179
+
180
+ assert_equal 201, hubot_setup("janky").status
181
+ end
182
+
183
+ test "hubot toggle" do
184
+ hubot_toggle("github")
185
+ gh_post_receive("github", "master", "deadbeef")
186
+ Janky::Builder.start!
187
+ Janky::Builder.complete!
188
+
189
+ assert Janky::Notifier.empty?
190
+
191
+ hubot_toggle("github")
192
+ gh_post_receive("github", "master", "cream")
193
+ Janky::Builder.start!
194
+ Janky::Builder.complete!
195
+
196
+ assert Janky::Notifier.success?("github", "master")
197
+ end
198
+
199
+ test "hubot status" do
200
+ gh_post_receive("github")
201
+ Janky::Builder.start!
202
+ Janky::Builder.complete!
203
+
204
+ status = hubot_status.body
205
+ assert status.include?("github")
206
+ assert status.include?("green")
207
+ assert status.include?("builds")
208
+
209
+ hubot_build("github", "master")
210
+ assert hubot_status.body.include?("green")
211
+
212
+ Janky::Builder.start!
213
+ assert hubot_status.body.include?("building")
214
+
215
+ hubot_setup("github/janky")
216
+ assert hubot_status.body.include?("no build")
217
+
218
+ hubot_setup("github/team")
219
+ gh_post_receive("team")
220
+ assert hubot_status.ok?
221
+ end
222
+
223
+ test "hubot status repo" do
224
+ gh_post_receive("github")
225
+ Janky::Builder.start!
226
+ Janky::Builder.complete!
227
+ hubot_build("github", "master")
228
+ Janky::Builder.start!
229
+ Janky::Builder.complete!
230
+
231
+ payload = Yajl.load(hubot_status("github", "master").body)
232
+
233
+ assert_equal 2, payload.size
234
+ end
235
+
236
+ test "hubot build" do
237
+ gh_post_receive("github", "master")
238
+ Janky::Builder.start!
239
+ Janky::Builder.complete!
240
+
241
+ assert hubot_build("github", "rails3").not_found?
242
+ end
243
+
244
+ test "hubot rooms" do
245
+ response = hubot_request("GET", "/_hubot/rooms")
246
+ rooms = Yajl.load(response.body)
247
+ assert_equal ["builds", "enterprise"], rooms
248
+ end
249
+
250
+ test "hubot set room" do
251
+ gh_post_receive("github", "master")
252
+ Janky::Builder.start!
253
+ Janky::Builder.complete!
254
+ assert Janky::Notifier.success?("github", "master", "builds")
255
+
256
+ Janky::Notifier.reset!
257
+
258
+ hubot_update_room("github", "enterprise").ok?
259
+ hubot_build("github", "master")
260
+ Janky::Builder.start!
261
+ Janky::Builder.complete!
262
+
263
+ assert Janky::Notifier.success?("github", "master", "enterprise")
264
+ end
265
+
266
+ test "hubot 404s" do
267
+ assert hubot_status("janky", "master").not_found?
268
+ assert hubot_build("janky", "master").not_found?
269
+ assert hubot_build("github", "master").not_found?
270
+ end
271
+ end