janky 0.9.13 → 0.9.14.rc1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,3 +1,11 @@
1
+ = 0.9.14 / 2012-??-??
2
+
3
+ * Make the Jenkins job name human readable [Riley Guerin]
4
+
5
+ * Added GithubStatus Notifier for GitHub Status API: see https://github.com/blog/1227-status-api
6
+
7
+ * Added Build#queued_at for tracking when builds were queued vs actually built
8
+
1
9
  = 0.9.13 / 2012-06-24
2
10
 
3
11
  * Pull down the branch HEAD commit from the GitHub API for manual builds.
data/README.md CHANGED
@@ -148,6 +148,17 @@ Using Janky with [GitHub Enterprise][ghe] requires one extra setting:
148
148
 
149
149
  [ghe]: https://enterprise.github.com
150
150
 
151
+ ### GitHub Status API
152
+
153
+ https://github.com/blog/1227-commit-status-api
154
+
155
+ To update pull requests with the build status generate an OAuth token
156
+ like so:
157
+
158
+ curl -u username:password -d '{ "scopes": [ "repo:status" ], "note": "janky" }' https://api.github.com/authorizations
159
+
160
+ then set `JANKY_GITHUB_STATUS_TOKEN`.
161
+
151
162
  ### Chat Notification
152
163
 
153
164
  #### Campfire
@@ -221,33 +232,33 @@ Hacking
221
232
 
222
233
  Get your environment up and running:
223
234
 
224
- $ script/bootstrap
235
+ script/bootstrap
225
236
 
226
237
  Create the databases:
227
238
 
228
- $ mysqladmin -uroot create janky_development
229
- $ mysqladmin -uroot create janky_test
239
+ mysqladmin -uroot create janky_development
240
+ mysqladmin -uroot create janky_test
230
241
 
231
242
  Create the tables:
232
243
 
233
- $ RACK_ENV=development bin/rake db:migrate
234
- $ RACK_ENV=test bin/rake db:migrate
244
+ RACK_ENV=development bin/rake db:migrate
245
+ RACK_ENV=test bin/rake db:migrate
235
246
 
236
247
  Seed some data into the development database:
237
248
 
238
- $ bin/rake db:seed
249
+ bin/rake db:seed
239
250
 
240
251
  Start the server:
241
252
 
242
- $ script/server
253
+ script/server
243
254
 
244
255
  Open the app:
245
256
 
246
- $ open http://localhost:9393/
257
+ open http://localhost:9393/
247
258
 
248
259
  Run the test suite:
249
260
 
250
- $ bin/rake
261
+ script/test
251
262
 
252
263
  Contributing
253
264
  ------------
data/Rakefile CHANGED
@@ -18,9 +18,7 @@ task "db:seed" do
18
18
  end
19
19
  end
20
20
 
21
- require "rake/testtask"
22
- Rake::TestTask.new(:test) do |t|
23
- t.test_files = FileList["test/*_test.rb"]
24
- t.ruby_opts << '-Itest -Ilib'
21
+ task :test do
22
+ abort "Use script/test to run the test suite."
25
23
  end
26
24
  task :default => :test
data/janky.gemspec CHANGED
@@ -25,6 +25,7 @@ Gem::Specification.new "janky", Janky::VERSION do |s|
25
25
 
26
26
  # test
27
27
  s.add_development_dependency "database_cleaner", "~>0.6"
28
+ s.add_development_dependency "mocha", "~>0.10.4"
28
29
 
29
30
  s.files = %w[
30
31
  CHANGES
@@ -64,6 +65,7 @@ lib/janky/database/migrate/1317384650_add_build_indexes.rb
64
65
  lib/janky/database/migrate/1317384651_add_more_build_indexes.rb
65
66
  lib/janky/database/migrate/1317384652_change_commit_message_to_text.rb
66
67
  lib/janky/database/migrate/1317384653_add_build_pusher.rb
68
+ lib/janky/database/migrate/1317384654_add_build_queued_at.rb
67
69
  lib/janky/database/schema.rb
68
70
  lib/janky/database/seed.dump.gz
69
71
  lib/janky/exception.rb
@@ -79,6 +81,7 @@ lib/janky/hubot.rb
79
81
  lib/janky/job_creator.rb
80
82
  lib/janky/notifier.rb
81
83
  lib/janky/notifier/chat_service.rb
84
+ lib/janky/notifier/github_status.rb
82
85
  lib/janky/notifier/mock.rb
83
86
  lib/janky/notifier/multi.rb
84
87
  lib/janky/public/css/base.css
data/lib/janky.rb CHANGED
@@ -46,6 +46,7 @@ require "janky/notifier"
46
46
  require "janky/notifier/chat_service"
47
47
  require "janky/notifier/mock"
48
48
  require "janky/notifier/multi"
49
+ require "janky/notifier/github_status"
49
50
  require "janky/app"
50
51
  require "janky/views/layout"
51
52
  require "janky/views/index"
@@ -97,6 +98,7 @@ module Janky
97
98
  settings["JANKY_BASE_URL"] = settings["JANKY_BASE_URL"] + "/"
98
99
  end
99
100
  base_url = URI(settings["JANKY_BASE_URL"]).to_s
101
+ Build.base_url = base_url
100
102
 
101
103
  connection = {
102
104
  :adapter => adapter,
@@ -197,7 +199,14 @@ module Janky
197
199
  end
198
200
  ChatService.setup(chat_name, chat_settings, chat_room)
199
201
 
200
- Notifier.setup(Notifier::ChatService)
202
+ if token = settings["JANKY_GITHUB_STATUS_TOKEN"]
203
+ Notifier.setup([
204
+ Notifier::GithubStatus.new(token, api_url),
205
+ Notifier::ChatService
206
+ ])
207
+ else
208
+ Notifier.setup(Notifier::ChatService)
209
+ end
201
210
  end
202
211
 
203
212
  # List of settings required in production.
@@ -212,10 +221,10 @@ module Janky
212
221
  JANKY_HUBOT_USER JANKY_HUBOT_PASSWORD]
213
222
  end
214
223
 
215
- # Directory where Jenkins job configuration templates are located.
216
- #
217
- # Returns the directory as a Pathname.
218
224
  class << self
225
+ # Directory where Jenkins job configuration templates are located.
226
+ #
227
+ # Returns the directory as a Pathname.
219
228
  attr_accessor :jobs_config_dir
220
229
  end
221
230
 
@@ -272,7 +281,6 @@ module Janky
272
281
 
273
282
  # Web dashboard
274
283
  map "/" do
275
- use Janky::NoAuth
276
284
  run Janky::App
277
285
  end
278
286
  }
data/lib/janky/app.rb CHANGED
@@ -40,7 +40,7 @@ module Janky
40
40
 
41
41
  get "/?" do
42
42
  authorize_index
43
- @builds = Build.started.first(50)
43
+ @builds = Build.queued.first(50)
44
44
  mustache :index
45
45
  end
46
46
 
@@ -54,7 +54,7 @@ module Janky
54
54
  repo = find_repo(repo_name)
55
55
  authorize_repo(repo)
56
56
 
57
- @builds = repo.builds.started.first(50)
57
+ @builds = repo.builds.queued.first(50)
58
58
  mustache :index
59
59
  end
60
60
 
@@ -62,20 +62,8 @@ module Janky
62
62
  repo = find_repo(repo_name)
63
63
  authorize_repo(repo)
64
64
 
65
- @builds = repo.branch_for(branch).builds.started.first(50)
65
+ @builds = repo.branch_for(branch).queued_builds.first(50)
66
66
  mustache :index
67
67
  end
68
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
69
  end
data/lib/janky/branch.rb CHANGED
@@ -46,6 +46,11 @@ module Janky
46
46
  builds.completed
47
47
  end
48
48
 
49
+ # See Build.queued.
50
+ def queued_builds
51
+ builds.queued
52
+ end
53
+
49
54
  # Create a build for the given commit.
50
55
  #
51
56
  # commit - the Janky::Commit instance to build.
data/lib/janky/build.rb CHANGED
@@ -39,6 +39,13 @@ module Janky
39
39
  end
40
40
  end
41
41
 
42
+ # Find all builds that have been queued in Jenkins, most recent first.
43
+ #
44
+ # Returns an Array of Build objects.
45
+ def self.queued
46
+ where("queued_at IS NOT NULL").order("queued_at DESC, id DESC")
47
+ end
48
+
42
49
  # Find all started builds, most recent first.
43
50
  #
44
51
  # Returns an Array of Builds.
@@ -61,6 +68,21 @@ module Janky
61
68
  completed.where(:green => true)
62
69
  end
63
70
 
71
+ # Has this build been queued in Jenkins?
72
+ #
73
+ # Returns true when the build is complete or currently being built,
74
+ # false otherwise.
75
+ def queued?
76
+ ! queued_at.nil?
77
+ end
78
+
79
+ # Is this build currently sitting in the queue waiting to be built?
80
+ #
81
+ # Returns true if the build is queued and not started, false otherwise.
82
+ def pending?
83
+ queued? && !started?
84
+ end
85
+
64
86
  # Is this build currently being built?
65
87
  #
66
88
  # Returns a Boolean.
@@ -94,6 +116,7 @@ module Janky
94
116
  # Returns nothing.
95
117
  def run
96
118
  builder.run(self)
119
+ update_attributes!(:queued_at => Time.now)
97
120
  end
98
121
 
99
122
  # See Repository#builder.
@@ -192,6 +215,30 @@ module Janky
192
215
  end
193
216
  end
194
217
 
218
+ class << self
219
+ # The full URL of the web app as a String, including the protocol.
220
+ attr_accessor :base_url
221
+
222
+ # The full URL to the Jenkins build page, as a String.
223
+ attr_reader :url
224
+ end
225
+
226
+ # URL of this build's web page, served by Janky::App.
227
+ #
228
+ # Returns the URL as a String.
229
+ def web_url
230
+ return if new_record?
231
+ self.class.base_url + "#{id}/output"
232
+ end
233
+
234
+ # URL of the web page for this build's branch, served by Janky::App.
235
+ #
236
+ # Returns the URL as a String.
237
+ def branch_url
238
+ return if new_record?
239
+ self.class.base_url + "#{repo_name}/#{branch_name}"
240
+ end
241
+
195
242
  def repo_id
196
243
  repository.id
197
244
  end
@@ -204,6 +251,10 @@ module Janky
204
251
  repository.name
205
252
  end
206
253
 
254
+ def repo_nwo
255
+ repository.nwo
256
+ end
257
+
207
258
  def repository
208
259
  branch.repository
209
260
  end
@@ -20,7 +20,7 @@ module Janky
20
20
 
21
21
  response = http.request(request)
22
22
 
23
- unless response.code == "302"
23
+ if response.code != "302"
24
24
  Exception.push_http_response(response)
25
25
  raise Error, "Failed to create build"
26
26
  end
@@ -19,8 +19,9 @@ module Janky
19
19
 
20
20
  def json_params
21
21
  Yajl.dump(:parameter => [
22
- { :name => "JANKY_SHA1", :value => @build.sha1 },
23
- { :name => "JANKY_ID", :value => @build.id }
22
+ { :name => "JANKY_SHA1", :value => @build.sha1 },
23
+ { :name => "JANKY_BRANCH", :value => @build.branch_name },
24
+ { :name => "JANKY_ID", :value => @build.id }
24
25
  ])
25
26
  end
26
27
 
@@ -0,0 +1,12 @@
1
+ class AddBuildQueuedAt < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :builds, :queued_at, :datetime, :null => true
4
+ Janky::Build.started.each do |b|
5
+ b.update_attributes!(:queued_at => b.created_at)
6
+ end
7
+ end
8
+
9
+ def self.down
10
+ remove_column :builds, :queued_at
11
+ end
12
+ end
@@ -11,7 +11,7 @@
11
11
  #
12
12
  # It's strongly recommended to check this file into your version control system.
13
13
 
14
- ActiveRecord::Schema.define(:version => 1317384652) do
14
+ ActiveRecord::Schema.define(:version => 1317384654) do
15
15
 
16
16
  create_table "branches", :force => true do |t|
17
17
  t.string "name", :null => false
@@ -35,6 +35,7 @@ ActiveRecord::Schema.define(:version => 1317384652) do
35
35
  t.text "output"
36
36
  t.integer "room_id"
37
37
  t.string "user"
38
+ t.datetime "queued_at"
38
39
  end
39
40
 
40
41
  add_index "builds", ["branch_id"], :name => "index_builds_on_branch_id"
data/lib/janky/hubot.rb CHANGED
@@ -86,13 +86,15 @@ module Janky
86
86
 
87
87
  repo = find_repo(repo_name)
88
88
  branch = repo.branch_for(branch_name)
89
- builds = branch.completed_builds.limit(limit).map do |build|
89
+ builds = branch.queued_builds.limit(limit).map do |build|
90
90
  { :sha1 => build.sha1,
91
91
  :repo => build.repo_name,
92
92
  :branch => build.branch_name,
93
93
  :user => build.user,
94
94
  :green => build.green?,
95
- :building => branch.building?,
95
+ :building => build.building?,
96
+ :queued => build.queued?,
97
+ :pending => build.pending?,
96
98
  :number => build.number,
97
99
  :status => (build.green? ? "was successful" : "failed"),
98
100
  :compare => build.compare,
@@ -12,7 +12,7 @@ module Janky
12
12
  build.branch_name,
13
13
  status,
14
14
  build.duration,
15
- build.compare
15
+ build.web_url
16
16
  ]
17
17
 
18
18
  ::Janky::ChatService.speak(message, build.room_id, {:color => color})
@@ -0,0 +1,57 @@
1
+ module Janky
2
+ module Notifier
3
+ # Create GitHub Status updates for builds.
4
+ #
5
+ # Note that Statuses are immutable - so we create one for
6
+ # "pending" status when a build starts, then create a new status for
7
+ # "success" or "failure" when the build is complete.
8
+ class GithubStatus
9
+ # Initialize with an OAuth token to POST Statuses with
10
+ def initialize(token, api_url)
11
+ @token = token
12
+ @api_url = URI(api_url)
13
+ end
14
+
15
+ # Create a Pending Status for the Commit when it starts.
16
+ def started(build)
17
+ repo = build.repo_nwo
18
+ path = "repos/#{repo}/statuses/#{build.sha1}"
19
+
20
+ post(path, "pending", build.web_url, "Build ##{build.number} started")
21
+ end
22
+
23
+ # Create a Success or Failure Status for the Commit.
24
+ def completed(build)
25
+ repo = build.repo_nwo
26
+ path = "repos/#{repo}/statuses/#{build.sha1}"
27
+ status = build.green? ? "success" : "failure"
28
+
29
+ desc = case status
30
+ when "success" then "Build ##{build.number} succeeded in #{build.duration}s"
31
+ when "failure" then "Build ##{build.number} failed in #{build.duration}s"
32
+ end
33
+
34
+ post(path, status, build.web_url, desc)
35
+ end
36
+
37
+ # Internal: POST the new status to the API
38
+ def post(path, status, url, desc)
39
+ http = Net::HTTP.new(@api_url.host, @api_url.port)
40
+ post = Net::HTTP::Post.new("#{@api_url.path}#{path}")
41
+
42
+ http.use_ssl = true
43
+
44
+ post["Content-Type"] = "application/json"
45
+ post["Authorization"] = "token #{@token}"
46
+
47
+ post.body = {
48
+ :state => status,
49
+ :target_url => url,
50
+ :description => desc,
51
+ }.to_json
52
+
53
+ http.request(post)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -146,6 +146,9 @@ ul.builds .building .status{
146
146
  ul.builds .janky .status{
147
147
  background-position:0 -200px;
148
148
  }
149
+ ul.builds .pending .status{
150
+ background-position:0 -100px;
151
+ }
149
152
 
150
153
  ul.builds h2{
151
154
  margin:0;
@@ -168,6 +171,12 @@ ul.builds .building a{
168
171
  ul.builds .building h2{
169
172
  color:#e59741;
170
173
  }
174
+ ul.builds .pending a{
175
+ color:#e59741;
176
+ }
177
+ ul.builds .pending h2{
178
+ color:#e59741;
179
+ }
171
180
  ul.builds .janky a{
172
181
  color:#ae0000;
173
182
  }
@@ -93,7 +93,7 @@ module Janky
93
93
  #
94
94
  # Returns the name as a String.
95
95
  def github_name
96
- uri[/.*[\/:]([a-zA-Z0-9\-_]+)\/([a-zA-Z0-9\-_]+)/] && $2
96
+ uri[/.*[\/:]([a-zA-Z0-9\-_]+)\/([a-zA-Z0-9\-_\.]+)/] && $2
97
97
  end
98
98
 
99
99
  # Fully qualified GitHub name for this repository.
@@ -130,7 +130,7 @@ module Janky
130
130
  read_attribute(:room_id) || ChatService.default_room_id
131
131
  end
132
132
 
133
- # Setups GitHub and Jenkins for build this repository.
133
+ # Setups GitHub and Jenkins for building this repository.
134
134
  #
135
135
  # Returns nothing.
136
136
  def setup
@@ -189,7 +189,7 @@ module Janky
189
189
  md5 << uri
190
190
  md5 << job_config_path.read
191
191
  md5 << builder.callback_url.to_s
192
- md5.hexdigest
192
+ "#{github_owner}-#{github_name}-#{md5.hexdigest}"
193
193
  end
194
194
  end
195
195
  end
@@ -1,4 +1,5 @@
1
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>
2
+ <a href="{{ repo_path }}">{{ repo_name }}</a>/<a href="{{ branch_path }}">{{ branch_name }}</a>/<a href="{{ commit_url }}">{{ commit_short_sha }}</a><br />
3
+ <a href="{{ jenkins_url }}">{{ jenkins_url }}</a>
3
4
  </p>
4
5
  <pre>{{ output }}</pre>
data/lib/janky/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Janky
2
- VERSION = "0.9.13"
2
+ VERSION = "0.9.14.rc1"
3
3
  end
@@ -28,6 +28,10 @@ module Janky
28
28
  def output
29
29
  @build.output
30
30
  end
31
+
32
+ def jenkins_url
33
+ @build.url
34
+ end
31
35
  end
32
36
  end
33
37
  end
@@ -25,7 +25,9 @@ module Janky
25
25
  "good"
26
26
  elsif build.building?
27
27
  "building"
28
- else
28
+ elsif build.pending?
29
+ "pending"
30
+ elsif build.red?
29
31
  "janky"
30
32
  end
31
33
  end
@@ -35,6 +37,8 @@ module Janky
35
37
  "Build started <span class='relatize'>#{build.started_at}</span>…"
36
38
  elsif build.completed?
37
39
  "Built in <span>#{build.duration}</span> seconds"
40
+ elsif build.pending?
41
+ "Build queued <span class='relatize'>#{build.queued_at}</span>…"
38
42
  end
39
43
  end
40
44
  end
data/test/janky_test.rb CHANGED
@@ -244,6 +244,13 @@ class JankyTest < Test::Unit::TestCase
244
244
 
245
245
  test "hubot status repo" do
246
246
  gh_post_receive("github")
247
+ payload = Yajl.load(hubot_status("github", "master").body)
248
+ assert_equal 1, payload.size
249
+ build = payload[0]
250
+ assert build["queued"]
251
+ assert build["pending"]
252
+ assert !build["building"]
253
+
247
254
  Janky::Builder.start!
248
255
  Janky::Builder.complete!
249
256
  hubot_build("github", "master")
@@ -310,15 +317,14 @@ class JankyTest < Test::Unit::TestCase
310
317
  assert hubot_build("github", "master").not_found?
311
318
  end
312
319
 
313
- test "github owner is parsed correctly" do
314
- repo = Janky::Repository.setup("github/janky")
315
- assert_equal "github", repo.github_owner
316
- assert_equal "janky", repo.github_name
317
- end
320
+ test "build janky url" do
321
+ gh_post_receive("github")
322
+ Janky::Builder.start!
323
+ Janky::Builder.complete!
324
+
325
+ assert_equal "http://localhost:9393/1/output", Janky::Build.last.web_url
318
326
 
319
- test "owner with a dash is parsed correctly" do
320
- repo = Janky::Repository.setup("digital-science/central-ftp-manage")
321
- assert_equal "digital-science", repo.github_owner
322
- assert_equal "central-ftp-manage", repo.github_name
327
+ build_page = Janky::Build.last.repo_job_name + "/" + Janky::Build.last.number + "/"
328
+ assert_equal "http://localhost:8080/job/" + build_page, Janky::Build.last.url
323
329
  end
324
330
  end
data/test/test_helper.rb CHANGED
@@ -2,6 +2,7 @@ $LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__))
2
2
 
3
3
  require "janky"
4
4
  require "test/unit"
5
+ require "mocha"
5
6
  require "database_cleaner"
6
7
 
7
8
  class Test::Unit::TestCase
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: janky
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.13
5
- prerelease:
4
+ version: 0.9.14.rc1
5
+ prerelease: 7
6
6
  platform: ruby
7
7
  authors:
8
8
  - Simon Rozet
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-24 00:00:00.000000000 Z
12
+ date: 2012-12-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -203,6 +203,22 @@ dependencies:
203
203
  - - ~>
204
204
  - !ruby/object:Gem::Version
205
205
  version: '0.6'
206
+ - !ruby/object:Gem::Dependency
207
+ name: mocha
208
+ requirement: !ruby/object:Gem::Requirement
209
+ none: false
210
+ requirements:
211
+ - - ~>
212
+ - !ruby/object:Gem::Version
213
+ version: 0.10.4
214
+ type: :development
215
+ prerelease: false
216
+ version_requirements: !ruby/object:Gem::Requirement
217
+ none: false
218
+ requirements:
219
+ - - ~>
220
+ - !ruby/object:Gem::Version
221
+ version: 0.10.4
206
222
  description: Janky is a Continuous Integration server
207
223
  email:
208
224
  executables: []
@@ -246,6 +262,7 @@ files:
246
262
  - lib/janky/database/migrate/1317384651_add_more_build_indexes.rb
247
263
  - lib/janky/database/migrate/1317384652_change_commit_message_to_text.rb
248
264
  - lib/janky/database/migrate/1317384653_add_build_pusher.rb
265
+ - lib/janky/database/migrate/1317384654_add_build_queued_at.rb
249
266
  - lib/janky/database/schema.rb
250
267
  - lib/janky/database/seed.dump.gz
251
268
  - lib/janky/exception.rb
@@ -261,6 +278,7 @@ files:
261
278
  - lib/janky/job_creator.rb
262
279
  - lib/janky/notifier.rb
263
280
  - lib/janky/notifier/chat_service.rb
281
+ - lib/janky/notifier/github_status.rb
264
282
  - lib/janky/notifier/mock.rb
265
283
  - lib/janky/notifier/multi.rb
266
284
  - lib/janky/public/css/base.css
@@ -298,9 +316,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
298
316
  required_rubygems_version: !ruby/object:Gem::Requirement
299
317
  none: false
300
318
  requirements:
301
- - - ! '>='
319
+ - - ! '>'
302
320
  - !ruby/object:Gem::Version
303
- version: '0'
321
+ version: 1.3.1
304
322
  requirements: []
305
323
  rubyforge_project:
306
324
  rubygems_version: 1.8.23