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