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.
@@ -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)
@@ -10,7 +10,7 @@ module Janky
10
10
  elsif payload.completed?
11
11
  Build.complete(payload.id, payload.green?)
12
12
  else
13
- return Rack::Response.new("Invalid", 402).finish
13
+ return Rack::Response.new("Bad Request", 400).finish
14
14
  end
15
15
 
16
16
  Rack::Response.new("OK", 201).finish
@@ -1,39 +1,48 @@
1
1
  module Janky
2
- # Sends messages to Campfire and accesses available rooms.
3
- module Campfire
4
- # Setup the Campfire client with the given credentials.
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
- # account - the Campfire account name as a String.
7
- # token - the Campfire API token as a String.
8
- # default - the name of the default Campfire room as a String.
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(account, token, default)
12
- ::Broach.settings = {
13
- "account" => account,
14
- "token" => token,
15
- "use_ssl" => true
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
- self.default_room_name = default
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 Campfire room.
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(room_name(room_id), message)
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 Broach::Room objects.
79
+ # Returns an Array of Room objects.
71
80
  def self.rooms
72
- @rooms ||= adapter.rooms
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, :default => 376289, :null => false # Builds room
6
+ t.integer :room_id, :null => false
7
7
  t.timestamps
8
8
  end
9
9
  add_index :repositories, :name, :unique => true
@@ -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
@@ -1,36 +1,43 @@
1
1
  module Janky
2
2
  module GitHub
3
- def self.setup(user, password, secret, url)
4
- @user = user
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 = secret
7
- @url = url
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, @url)
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
@@ -1,13 +1,14 @@
1
1
  module Janky
2
2
  module GitHub
3
3
  class API
4
- def initialize(user, password)
5
- @user = user
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("/repos/#{nwo}/hooks")
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("#{path}/test")
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 = "/repos/#{nwo}"
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("https://api.github.com")
67
+ uri = URI(@url)
59
68
  http = Net::HTTP.new(uri.host, uri.port)
60
69
 
61
70
  http.use_ssl = true