janky 0.9.0 → 0.9.9
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.
- data/CHANGES +30 -0
- data/README.md +59 -21
- data/Rakefile +7 -0
- data/janky.gemspec +6 -4
- data/lib/janky.rb +81 -22
- data/lib/janky/app.rb +1 -1
- data/lib/janky/build.rb +9 -1
- data/lib/janky/builder/http.rb +8 -0
- data/lib/janky/builder/receiver.rb +1 -1
- data/lib/janky/{campfire.rb → chat_service.rb} +28 -55
- data/lib/janky/chat_service/campfire.rb +33 -0
- data/lib/janky/chat_service/hipchat.rb +29 -0
- data/lib/janky/chat_service/mock.rb +26 -0
- data/lib/janky/database/migrate/1312115512_init.rb +1 -1
- data/lib/janky/exception.rb +23 -0
- data/lib/janky/github.rb +82 -23
- data/lib/janky/github/api.rb +17 -8
- data/lib/janky/github/payload_parser.rb +1 -1
- data/lib/janky/hubot.rb +15 -14
- data/lib/janky/job_creator.rb +10 -2
- data/lib/janky/notifier/{campfire.rb → chat_service.rb} +4 -3
- data/lib/janky/notifier/mock.rb +2 -2
- data/lib/janky/public/css/base.css +33 -10
- data/lib/janky/repository.rb +4 -4
- data/lib/janky/templates/index.mustache +10 -5
- data/lib/janky/version.rb +1 -1
- data/lib/janky/views/index.rb +10 -3
- data/lib/janky/views/layout.rb +1 -0
- data/test/janky_test.rb +14 -2
- data/test/test_helper.rb +5 -4
- metadata +13 -10
- data/lib/janky/campfire/mock.rb +0 -0
data/lib/janky/builder/http.rb
CHANGED
@@ -8,6 +8,10 @@ module Janky
|
|
8
8
|
|
9
9
|
def run(params, create_url)
|
10
10
|
http = Net::HTTP.new(create_url.host, create_url.port)
|
11
|
+
if create_url.scheme == "https"
|
12
|
+
http.use_ssl = true
|
13
|
+
end
|
14
|
+
|
11
15
|
request = Net::HTTP::Post.new(create_url.path)
|
12
16
|
if @username && @password
|
13
17
|
request.basic_auth(@username, @password)
|
@@ -24,6 +28,10 @@ module Janky
|
|
24
28
|
|
25
29
|
def output(url)
|
26
30
|
http = Net::HTTP.new(url.host, url.port)
|
31
|
+
if url.scheme == "https"
|
32
|
+
http.use_ssl = true
|
33
|
+
end
|
34
|
+
|
27
35
|
request = Net::HTTP::Get.new(url.path)
|
28
36
|
if @username && @password
|
29
37
|
request.basic_auth(@username, @password)
|
@@ -1,39 +1,48 @@
|
|
1
1
|
module Janky
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
module ChatService
|
3
|
+
Room = Struct.new(:id, :name)
|
4
|
+
|
5
|
+
# Setup the adapter used to notify chat rooms of build status.
|
5
6
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# default
|
7
|
+
# name - Service name as a string.
|
8
|
+
# settings - Service-specific setting hash.
|
9
|
+
# default - Name of the default chat room as a String.
|
9
10
|
#
|
10
11
|
# Returns nothing.
|
11
|
-
def self.setup(
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
"
|
16
|
-
|
12
|
+
def self.setup(name, settings, default)
|
13
|
+
klass = adapters[name]
|
14
|
+
|
15
|
+
if !klass
|
16
|
+
raise Error, "Unknown chat service: #{name.inspect}. Available " \
|
17
|
+
"services are #{adapters.keys.join(", ")}"
|
18
|
+
end
|
17
19
|
|
18
|
-
|
20
|
+
@adapter = klass.new(settings)
|
21
|
+
@default_room_name = default
|
19
22
|
end
|
20
23
|
|
21
24
|
class << self
|
22
|
-
attr_accessor :default_room_name
|
25
|
+
attr_accessor :adapter, :default_room_name
|
26
|
+
end
|
27
|
+
|
28
|
+
# Registry of available chat implementations.
|
29
|
+
def self.adapters
|
30
|
+
@adapters ||= {}
|
23
31
|
end
|
24
32
|
|
25
33
|
def self.default_room_id
|
26
34
|
room_id(default_room_name)
|
27
35
|
end
|
28
36
|
|
29
|
-
# Send a message to a
|
37
|
+
# Send a message to a Chat room.
|
30
38
|
#
|
31
39
|
# message - The String message.
|
32
40
|
# room_id - The Integer room ID.
|
41
|
+
# options - Optional hash passed to the chat adapter.
|
33
42
|
#
|
34
43
|
# Returns nothing.
|
35
|
-
def self.speak(message, room_id)
|
36
|
-
adapter.speak(
|
44
|
+
def self.speak(message, room_id, options = {})
|
45
|
+
adapter.speak(message, room_id, options)
|
37
46
|
end
|
38
47
|
|
39
48
|
# Get the ID of a room.
|
@@ -67,9 +76,9 @@ module Janky
|
|
67
76
|
|
68
77
|
# Memoized list of available rooms.
|
69
78
|
#
|
70
|
-
# Returns an Array of
|
79
|
+
# Returns an Array of Room objects.
|
71
80
|
def self.rooms
|
72
|
-
|
81
|
+
adapter.rooms
|
73
82
|
end
|
74
83
|
|
75
84
|
# Enable mocking. Once enabled, messages are discarded.
|
@@ -87,41 +96,5 @@ module Janky
|
|
87
96
|
def self.rooms=(value)
|
88
97
|
adapter.rooms = value
|
89
98
|
end
|
90
|
-
|
91
|
-
def self.adapter
|
92
|
-
@adapter ||= Broach.new
|
93
|
-
end
|
94
|
-
|
95
|
-
class Broach
|
96
|
-
def speak(room_name, message)
|
97
|
-
::Broach.speak(room_name, message)
|
98
|
-
end
|
99
|
-
|
100
|
-
def rooms
|
101
|
-
::Broach.rooms
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
class Mock
|
106
|
-
def initialize
|
107
|
-
@rooms = {}
|
108
|
-
end
|
109
|
-
|
110
|
-
attr_writer :rooms
|
111
|
-
|
112
|
-
def speak(room_name, message)
|
113
|
-
if !@rooms.values.include?(room_name)
|
114
|
-
raise Error, "Unknown room #{room_name.inspect}"
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def rooms
|
119
|
-
acc = []
|
120
|
-
@rooms.each do |id, name|
|
121
|
-
acc << ::Broach::Room.new("id" => id, "name" => name)
|
122
|
-
end
|
123
|
-
acc
|
124
|
-
end
|
125
|
-
end
|
126
99
|
end
|
127
100
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Janky
|
2
|
+
module ChatService
|
3
|
+
class Campfire
|
4
|
+
def initialize(settings)
|
5
|
+
account = settings["JANKY_CHAT_CAMPFIRE_ACCOUNT"]
|
6
|
+
if account.nil? || account.empty?
|
7
|
+
raise Error, "JANKY_CHAT_CAMPFIRE_ACCOUNT setting is required"
|
8
|
+
end
|
9
|
+
|
10
|
+
token = settings["JANKY_CHAT_CAMPFIRE_TOKEN"]
|
11
|
+
if token.nil? || token.empty?
|
12
|
+
raise Error, "JANKY_CHAT_CAMPFIRE_TOKEN setting is required"
|
13
|
+
end
|
14
|
+
|
15
|
+
Broach.settings = {
|
16
|
+
"account" => account,
|
17
|
+
"token" => token,
|
18
|
+
"use_ssl" => true,
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def speak(message, room_id, opts={})
|
23
|
+
Broach.speak(ChatService.room_name(room_id), message)
|
24
|
+
end
|
25
|
+
|
26
|
+
def rooms
|
27
|
+
@rooms ||= Broach.rooms.map do |room|
|
28
|
+
Room.new(room.id, room.name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "hipchat"
|
2
|
+
|
3
|
+
module Janky
|
4
|
+
module ChatService
|
5
|
+
class HipChat
|
6
|
+
def initialize(settings)
|
7
|
+
token = settings["JANKY_CHAT_HIPCHAT_TOKEN"]
|
8
|
+
if token.nil? || token.empty?
|
9
|
+
raise Error, "JANKY_CHAT_HIPCHAT_TOKEN setting is required"
|
10
|
+
end
|
11
|
+
|
12
|
+
@client = ::HipChat::Client.new(token)
|
13
|
+
@from = settings["JANKY_CHAT_HIPCHAT_FROM"] || "CI"
|
14
|
+
end
|
15
|
+
|
16
|
+
def speak(message, room_id, options = {:color => "yellow"})
|
17
|
+
@client[room_id].send(@from, message, options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def rooms
|
21
|
+
@rooms ||= @client.rooms.map do |room|
|
22
|
+
Room.new(room.room_id, room.name)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
register_chat_service "hipchat", ChatService::HipChat
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Janky
|
2
|
+
module ChatService
|
3
|
+
# Mock chat implementation used in testing environments.
|
4
|
+
class Mock
|
5
|
+
def initialize
|
6
|
+
@rooms = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_writer :rooms
|
10
|
+
|
11
|
+
def speak(room_name, message)
|
12
|
+
if !@rooms.values.include?(room_name)
|
13
|
+
raise Error, "Unknown room #{room_name.inspect}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def rooms
|
18
|
+
acc = []
|
19
|
+
@rooms.each do |id, name|
|
20
|
+
acc << Room.new(id, name)
|
21
|
+
end
|
22
|
+
acc
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -3,7 +3,7 @@ class Init < ActiveRecord::Migration
|
|
3
3
|
create_table :repositories, :force => true do |t|
|
4
4
|
t.string :name, :null => false
|
5
5
|
t.string :uri, :null => false
|
6
|
-
t.integer :room_id, :
|
6
|
+
t.integer :room_id, :null => false
|
7
7
|
t.timestamps
|
8
8
|
end
|
9
9
|
add_index :repositories, :name, :unique => true
|
data/lib/janky/exception.rb
CHANGED
@@ -48,6 +48,29 @@ module Janky
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
+
class Logger
|
52
|
+
def initialize(stream)
|
53
|
+
@stream = stream
|
54
|
+
@context = {}
|
55
|
+
end
|
56
|
+
|
57
|
+
def reset!
|
58
|
+
@context = {}
|
59
|
+
end
|
60
|
+
|
61
|
+
def report(e, context={})
|
62
|
+
@stream.puts "ERROR: #{e.class} - #{e.message}\n"
|
63
|
+
@context.each do |k, v|
|
64
|
+
@stream.puts "%12s %4s\n" % [k, v]
|
65
|
+
end
|
66
|
+
@stream.puts "\n#{e.backtrace.join("\n")}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def push(context)
|
70
|
+
@context.update(context)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
51
74
|
class Mock
|
52
75
|
def self.push(context)
|
53
76
|
end
|
data/lib/janky/github.rb
CHANGED
@@ -1,36 +1,43 @@
|
|
1
1
|
module Janky
|
2
2
|
module GitHub
|
3
|
-
|
4
|
-
|
3
|
+
# Setup the GitHub API client and Post-Receive hook endpoint.
|
4
|
+
#
|
5
|
+
# user - API user as a String.
|
6
|
+
# password - API password as a String.
|
7
|
+
# secret - Secret used to sign hook requests from GitHub.
|
8
|
+
# hook_url - String URL that handles Post-Receive requests.
|
9
|
+
# api_url - GitHub API URL as a String. Requires a trailing slash.
|
10
|
+
# git_host - Hostname where git repos are hosted. e.g. "github.com"
|
11
|
+
#
|
12
|
+
# Returns nothing.
|
13
|
+
def self.setup(user, password, secret, hook_url, api_url, git_host)
|
14
|
+
@user = user
|
5
15
|
@password = password
|
6
|
-
@secret
|
7
|
-
@
|
16
|
+
@secret = secret
|
17
|
+
@hook_url = hook_url
|
18
|
+
@api_url = api_url
|
19
|
+
@git_host = git_host
|
8
20
|
end
|
9
21
|
|
10
22
|
class << self
|
11
|
-
attr_reader :secret
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.enable_mock!
|
15
|
-
@api = Mock.new(@user, @password)
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.repo_make_private(nwo)
|
19
|
-
api.make_private(nwo)
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.repo_make_public(nwo)
|
23
|
-
api.make_public(nwo)
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.repo_make_unauthorized(nwo)
|
27
|
-
api.make_unauthorized(nwo)
|
23
|
+
attr_reader :secret, :git_host
|
28
24
|
end
|
29
25
|
|
26
|
+
# Rack app that handles Post-Receive hook requests from GitHub.
|
27
|
+
#
|
28
|
+
# Returns a GitHub::Receiver.
|
30
29
|
def self.receiver
|
31
30
|
@receiver ||= Receiver.new(@secret)
|
32
31
|
end
|
33
32
|
|
33
|
+
# Fetch repository details.
|
34
|
+
# http://developer.github.com/v3/repos/#get
|
35
|
+
#
|
36
|
+
# nwo - qualified "owner/repo" name.
|
37
|
+
#
|
38
|
+
# Returns the Hash representation of the repo, nil when it doesn't exists
|
39
|
+
# or access was denied.
|
40
|
+
# Raises an Error for any unexpected response.
|
34
41
|
def self.repo_get(nwo)
|
35
42
|
response = api.repo_get(nwo)
|
36
43
|
|
@@ -45,8 +52,15 @@ module Janky
|
|
45
52
|
end
|
46
53
|
end
|
47
54
|
|
55
|
+
# Create a Post-Receive hook for the given repository.
|
56
|
+
# http://developer.github.com/v3/repos/hooks/#create-a-hook
|
57
|
+
#
|
58
|
+
# nwo - qualified "owner/repo" name.
|
59
|
+
#
|
60
|
+
# Returns the newly created hook URL as String when successful.
|
61
|
+
# Raises an Error for any other response.
|
48
62
|
def self.hook_create(nwo)
|
49
|
-
response = api.create(nwo, @secret, @
|
63
|
+
response = api.create(nwo, @secret, @hook_url)
|
50
64
|
|
51
65
|
if response.code == "201"
|
52
66
|
Yajl.load(response.body)["url"]
|
@@ -56,12 +70,57 @@ module Janky
|
|
56
70
|
end
|
57
71
|
end
|
58
72
|
|
73
|
+
# Check existance of a hook.
|
74
|
+
# http://developer.github.com/v3/repos/hooks/#get-single-hook
|
75
|
+
#
|
76
|
+
# url - Hook URL as a String.
|
59
77
|
def self.hook_exists?(url)
|
60
78
|
api.get(url).code == "200"
|
61
79
|
end
|
62
80
|
|
81
|
+
# Default API implementation that goes over the wire (HTTP).
|
82
|
+
#
|
83
|
+
# Returns nothing.
|
63
84
|
def self.api
|
64
|
-
@api ||= API.new(@user, @password)
|
85
|
+
@api ||= API.new(@api_url, @user, @password)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Turn on mock mode, meaning no request goes over the wire. Useful in
|
89
|
+
# testing environments.
|
90
|
+
#
|
91
|
+
# Returns nothing.
|
92
|
+
def self.enable_mock!
|
93
|
+
@api = Mock.new(@user, @password)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Make any subsequent response for the given repository look like as if
|
97
|
+
# it was a private repo.
|
98
|
+
#
|
99
|
+
# nwo - qualified "owner/repo" name.
|
100
|
+
#
|
101
|
+
# Returns nothing.
|
102
|
+
def self.repo_make_private(nwo)
|
103
|
+
api.make_private(nwo)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Make any subsequent request to the given repository succeed. Only
|
107
|
+
# available in mock mode.
|
108
|
+
#
|
109
|
+
# nwo - qualified "owner/repo" name.
|
110
|
+
#
|
111
|
+
# Returns nothing.
|
112
|
+
def self.repo_make_public(nwo)
|
113
|
+
api.make_public(nwo)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Make any subsequent request for the given repository fail with an
|
117
|
+
# unauthorized response. Only available when mocked.
|
118
|
+
#
|
119
|
+
# nwo - qualified "owner/repo" name.
|
120
|
+
#
|
121
|
+
# Returns nothing.
|
122
|
+
def self.repo_make_unauthorized(nwo)
|
123
|
+
api.make_unauthorized(nwo)
|
65
124
|
end
|
66
125
|
end
|
67
126
|
end
|
data/lib/janky/github/api.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
module Janky
|
2
2
|
module GitHub
|
3
3
|
class API
|
4
|
-
def initialize(user, password)
|
5
|
-
@
|
4
|
+
def initialize(url, user, password)
|
5
|
+
@url = url
|
6
|
+
@user = user
|
6
7
|
@password = password
|
7
8
|
end
|
8
9
|
|
9
10
|
def create(nwo, secret, url)
|
10
|
-
request = Net::HTTP::Post.new("
|
11
|
+
request = Net::HTTP::Post.new(build_path("repos/#{nwo}/hooks"))
|
11
12
|
payload = build_payload(url, secret)
|
12
13
|
request.body = Yajl.dump(payload)
|
13
14
|
request.basic_auth(@user, @password)
|
@@ -16,15 +17,15 @@ module Janky
|
|
16
17
|
end
|
17
18
|
|
18
19
|
def trigger(hook_url)
|
19
|
-
path = URI(hook_url).path
|
20
|
-
request = Net::HTTP::Post.new(
|
20
|
+
path = build_path(URI(hook_url).path + "/test")
|
21
|
+
request = Net::HTTP::Post.new(path)
|
21
22
|
request.basic_auth(@user, @password)
|
22
23
|
|
23
24
|
http.request(request)
|
24
25
|
end
|
25
26
|
|
26
27
|
def get(hook_url)
|
27
|
-
path = URI(hook_url).path
|
28
|
+
path = build_path(URI(hook_url).path)
|
28
29
|
request = Net::HTTP::Get.new(path)
|
29
30
|
request.basic_auth(@user, @password)
|
30
31
|
|
@@ -32,13 +33,21 @@ module Janky
|
|
32
33
|
end
|
33
34
|
|
34
35
|
def repo_get(nwo)
|
35
|
-
path = "
|
36
|
+
path = build_path("repos/#{nwo}")
|
36
37
|
request = Net::HTTP::Get.new(path)
|
37
38
|
request.basic_auth(@user, @password)
|
38
39
|
|
39
40
|
http.request(request)
|
40
41
|
end
|
41
42
|
|
43
|
+
def build_path(path)
|
44
|
+
if path[0] == ?/
|
45
|
+
URI.join(@url, path[1..-1]).path
|
46
|
+
else
|
47
|
+
URI.join(@url, path).path
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
42
51
|
def build_payload(url, secret)
|
43
52
|
{ "name" => "web",
|
44
53
|
"active" => true,
|
@@ -55,7 +64,7 @@ module Janky
|
|
55
64
|
end
|
56
65
|
|
57
66
|
def http!
|
58
|
-
uri = URI(
|
67
|
+
uri = URI(@url)
|
59
68
|
http = Net::HTTP.new(uri.host, uri.port)
|
60
69
|
|
61
70
|
http.use_ssl = true
|