janky 0.10.2 → 0.11.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.
- checksums.yaml +4 -4
- data/CHANGES +12 -0
- data/README.md +48 -5
- data/janky.gemspec +6 -1
- data/lib/janky.rb +7 -3
- data/lib/janky/branch.rb +4 -20
- data/lib/janky/build.rb +3 -3
- data/lib/janky/build_request.rb +1 -0
- data/lib/janky/builder/payload.rb +1 -1
- data/lib/janky/chat_service.rb +3 -3
- data/lib/janky/chat_service/slack.rb +65 -0
- data/lib/janky/commit.rb +17 -0
- data/lib/janky/database/migrate/1317384655_add_template.rb +9 -0
- data/lib/janky/database/migrate/1398262033_add_context.rb +9 -0
- data/lib/janky/database/migrate/1400144784_change_room_id_to_string.rb +6 -0
- data/lib/janky/database/schema.rb +17 -15
- data/lib/janky/github.rb +19 -9
- data/lib/janky/github/api.rb +10 -2
- data/lib/janky/github/mock.rb +6 -12
- data/lib/janky/hubot.rb +59 -3
- data/lib/janky/notifier.rb +9 -0
- data/lib/janky/notifier/chat_service.rb +1 -1
- data/lib/janky/notifier/failure_service.rb +18 -0
- data/lib/janky/notifier/github_status.rb +30 -6
- data/lib/janky/notifier/mock.rb +3 -0
- data/lib/janky/notifier/multi.rb +6 -0
- data/lib/janky/public/css/base.css +0 -4
- data/lib/janky/repository.rb +60 -12
- data/lib/janky/version.rb +1 -1
- data/test/janky_test.rb +15 -1
- metadata +38 -34
data/lib/janky/github.rb
CHANGED
@@ -67,16 +67,10 @@ module Janky
|
|
67
67
|
#
|
68
68
|
# Returns the SHA1 as a String or nil when the branch doesn't exists.
|
69
69
|
def self.branch_head_sha(nwo, branch)
|
70
|
-
response = api.
|
70
|
+
response = api.branch(nwo, branch)
|
71
71
|
|
72
|
-
|
73
|
-
|
74
|
-
raise Error, "Failed to get branches"
|
75
|
-
end
|
76
|
-
|
77
|
-
branches = Yajl.load(response.body)
|
78
|
-
branch = branches.detect { |b| b["name"] == branch }
|
79
|
-
branch && branch["commit"]["sha"]
|
72
|
+
branch = Yajl.load(response.body)
|
73
|
+
branch && branch["sha"]
|
80
74
|
end
|
81
75
|
|
82
76
|
# Fetch commit details for the given SHA1.
|
@@ -134,6 +128,22 @@ module Janky
|
|
134
128
|
api.get(url).code == "200"
|
135
129
|
end
|
136
130
|
|
131
|
+
# Delete a post-receive hook for the given repository.
|
132
|
+
#
|
133
|
+
# hook_url - The repository's hook_url
|
134
|
+
#
|
135
|
+
# Returns true or raises an exception.
|
136
|
+
def self.hook_delete(url)
|
137
|
+
response = api.delete(url)
|
138
|
+
|
139
|
+
if response.code == "204"
|
140
|
+
true
|
141
|
+
else
|
142
|
+
Exception.push_http_response(response)
|
143
|
+
raise Error, "Failed to delete hook"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
137
147
|
# Default API implementation that goes over the wire (HTTP).
|
138
148
|
#
|
139
149
|
# Returns nothing.
|
data/lib/janky/github/api.rb
CHANGED
@@ -16,6 +16,14 @@ module Janky
|
|
16
16
|
http.request(request)
|
17
17
|
end
|
18
18
|
|
19
|
+
def delete(hook_url)
|
20
|
+
path = build_path(URI(hook_url).path)
|
21
|
+
request = Net::HTTP::Delete.new(path)
|
22
|
+
request.basic_auth(@user, @password)
|
23
|
+
|
24
|
+
http.request(request)
|
25
|
+
end
|
26
|
+
|
19
27
|
def trigger(hook_url)
|
20
28
|
path = build_path(URI(hook_url).path + "/test")
|
21
29
|
request = Net::HTTP::Post.new(path)
|
@@ -40,8 +48,8 @@ module Janky
|
|
40
48
|
http.request(request)
|
41
49
|
end
|
42
50
|
|
43
|
-
def
|
44
|
-
path = build_path("repos/#{nwo}/
|
51
|
+
def branch(nwo, branch)
|
52
|
+
path = build_path("repos/#{nwo}/commits/#{branch}")
|
45
53
|
request = Net::HTTP::Get.new(path)
|
46
54
|
request.basic_auth(@user, @password)
|
47
55
|
|
data/lib/janky/github/mock.rb
CHANGED
@@ -33,6 +33,10 @@ module Janky
|
|
33
33
|
Response.new("200")
|
34
34
|
end
|
35
35
|
|
36
|
+
def delete(url)
|
37
|
+
Response.new("204")
|
38
|
+
end
|
39
|
+
|
36
40
|
def repo_get(nwo)
|
37
41
|
repo = {
|
38
42
|
"name" => nwo.split("/").last,
|
@@ -48,19 +52,9 @@ module Janky
|
|
48
52
|
end
|
49
53
|
end
|
50
54
|
|
51
|
-
def
|
52
|
-
data = []
|
55
|
+
def branch(nwo, branch)
|
53
56
|
|
54
|
-
@branch_shas
|
55
|
-
if nwo == name_with_owner
|
56
|
-
data << {
|
57
|
-
"name" => branch,
|
58
|
-
"commit" => {
|
59
|
-
"sha" => sha
|
60
|
-
}
|
61
|
-
}
|
62
|
-
end
|
63
|
-
end
|
57
|
+
data = { "sha" => @branch_shas[[nwo, branch]] }
|
64
58
|
|
65
59
|
Response.new("200", Yajl.dump(data))
|
66
60
|
end
|
data/lib/janky/hubot.rb
CHANGED
@@ -12,11 +12,12 @@ module Janky
|
|
12
12
|
post "/setup" do
|
13
13
|
nwo = params["nwo"]
|
14
14
|
name = params["name"]
|
15
|
-
|
15
|
+
tmpl = params["template"]
|
16
|
+
repo = Repository.setup(nwo, name, tmpl)
|
16
17
|
|
17
18
|
if repo
|
18
19
|
url = "#{settings.base_url}#{repo.name}"
|
19
|
-
[201, "Setup #{repo.name} at #{repo.uri} | #{url}"]
|
20
|
+
[201, "Setup #{repo.name} at #{repo.uri} with #{repo.job_config_path.basename} | #{url}"]
|
20
21
|
else
|
21
22
|
[400, "Couldn't access #{nwo}. Check the permissions."]
|
22
23
|
end
|
@@ -34,9 +35,10 @@ module Janky
|
|
34
35
|
post %r{\/([-_\.0-9a-zA-Z]+)\/([-_\.a-zA-z0-9\/]+)} do |repo_name, branch_name|
|
35
36
|
repo = find_repo(repo_name)
|
36
37
|
branch = repo.branch_for(branch_name)
|
37
|
-
room_id = (params["room_id"]
|
38
|
+
room_id = (params["room_id"] rescue nil)
|
38
39
|
user = params["user"]
|
39
40
|
build = branch.head_build_for(room_id, user)
|
41
|
+
build ||= repo.build_sha(branch_name, user, room_id)
|
40
42
|
|
41
43
|
if build
|
42
44
|
build.run
|
@@ -64,6 +66,20 @@ module Janky
|
|
64
66
|
end
|
65
67
|
end
|
66
68
|
|
69
|
+
# Update a repository's context
|
70
|
+
put %r{\/([-_\.0-9a-zA-Z]+)\/context} do |repo_name|
|
71
|
+
context = params["context"]
|
72
|
+
repo = find_repo(repo_name)
|
73
|
+
|
74
|
+
if repo
|
75
|
+
repo.context = context
|
76
|
+
repo.save
|
77
|
+
[200, "Context #{context} set for #{repo_name}"]
|
78
|
+
else
|
79
|
+
[404, "Unknown Repository #{repo_name}"]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
67
83
|
# Get the status of all projects.
|
68
84
|
get "/" do
|
69
85
|
content_type "text/plain"
|
@@ -100,6 +116,41 @@ module Janky
|
|
100
116
|
builds.to_json
|
101
117
|
end
|
102
118
|
|
119
|
+
# Get information about how a project is configured
|
120
|
+
get %r{\/show\/([-_\.0-9a-zA-Z]+)} do |repo_name|
|
121
|
+
repo = find_repo(repo_name)
|
122
|
+
res = {
|
123
|
+
:name => repo.name,
|
124
|
+
:configured_job_template => repo.job_template,
|
125
|
+
:used_job_template => repo.job_config_path.basename.to_s,
|
126
|
+
:repo => repo.uri,
|
127
|
+
:room_id => repo.room_id,
|
128
|
+
:enabled => repo.enabled,
|
129
|
+
:hook_url => repo.hook_url,
|
130
|
+
:context => repo.context
|
131
|
+
}
|
132
|
+
res.to_json
|
133
|
+
end
|
134
|
+
|
135
|
+
delete %r{\/([-_\.0-9a-zA-Z]+)} do |repo_name|
|
136
|
+
repo = find_repo(repo_name)
|
137
|
+
repo.destroy
|
138
|
+
"Janky project #{repo_name} deleted"
|
139
|
+
end
|
140
|
+
|
141
|
+
# Delete a repository's context
|
142
|
+
delete %r{\/([-_\.0-9a-zA-Z]+)\/context} do |repo_name|
|
143
|
+
repo = find_repo(repo_name)
|
144
|
+
|
145
|
+
if repo
|
146
|
+
repo.context = nil
|
147
|
+
repo.save
|
148
|
+
[200, "Context removed for #{repo_name}"]
|
149
|
+
else
|
150
|
+
[404, "Unknown Repository #{repo_name}"]
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
103
154
|
# Get the status of a repository's branch.
|
104
155
|
get %r{\/([-_\.0-9a-zA-Z]+)\/([-_\+\.a-zA-z0-9\/]+)} do |repo_name, branch_name|
|
105
156
|
limit = params["limit"]
|
@@ -120,13 +171,18 @@ module Janky
|
|
120
171
|
ci build janky
|
121
172
|
ci build janky/fix-everything
|
122
173
|
ci setup github/janky [name]
|
174
|
+
ci setup github/janky name template
|
123
175
|
ci toggle janky
|
124
176
|
ci rooms
|
125
177
|
ci set room janky development
|
178
|
+
ci set context janky ci/janky
|
179
|
+
ci unset context janky
|
126
180
|
ci status
|
127
181
|
ci status janky
|
128
182
|
ci status janky/master
|
129
183
|
ci builds limit [building]
|
184
|
+
ci show janky
|
185
|
+
ci delete janky
|
130
186
|
EOS
|
131
187
|
end
|
132
188
|
|
data/lib/janky/notifier.rb
CHANGED
@@ -9,6 +9,15 @@ module Janky
|
|
9
9
|
@adapter = Multi.new(Array(notifiers))
|
10
10
|
end
|
11
11
|
|
12
|
+
# Called whenever a build is queued
|
13
|
+
#
|
14
|
+
# build - the Build record.
|
15
|
+
#
|
16
|
+
# Returns nothing
|
17
|
+
def self.queued(build)
|
18
|
+
adapter.queued(build)
|
19
|
+
end
|
20
|
+
|
12
21
|
# Called whenever a build starts.
|
13
22
|
#
|
14
23
|
# build - the Build record.
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Janky
|
2
|
+
module Notifier
|
3
|
+
class FailureService < ChatService
|
4
|
+
def self.completed(build)
|
5
|
+
return unless need_failure_notification?(build)
|
6
|
+
::Janky::ChatService.speak(message(build), failure_room, {:color => color(build)})
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.failure_room
|
10
|
+
ENV["JANKY_CHAT_FAILURE_ROOM"]
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.need_failure_notification?(build)
|
14
|
+
build.red? && failure_room != build.room_id
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -7,9 +7,26 @@ module Janky
|
|
7
7
|
# "success" or "failure" when the build is complete.
|
8
8
|
class GithubStatus
|
9
9
|
# Initialize with an OAuth token to POST Statuses with
|
10
|
-
def initialize(token, api_url)
|
10
|
+
def initialize(token, api_url, context = nil)
|
11
11
|
@token = token
|
12
12
|
@api_url = URI(api_url)
|
13
|
+
@default_context = context
|
14
|
+
end
|
15
|
+
|
16
|
+
def context(build)
|
17
|
+
repository_context(build.repository) || @default_context
|
18
|
+
end
|
19
|
+
|
20
|
+
def repository_context(repository)
|
21
|
+
repository && repository.context
|
22
|
+
end
|
23
|
+
|
24
|
+
# Create a Pending Status for the Commit when it is queued.
|
25
|
+
def queued(build)
|
26
|
+
repo = build.repo_nwo
|
27
|
+
path = "repos/#{repo}/statuses/#{build.sha1}"
|
28
|
+
|
29
|
+
post(path, "pending", build.web_url, "Build ##{build.number} queued", context(build))
|
13
30
|
end
|
14
31
|
|
15
32
|
# Create a Pending Status for the Commit when it starts.
|
@@ -17,7 +34,7 @@ module Janky
|
|
17
34
|
repo = build.repo_nwo
|
18
35
|
path = "repos/#{repo}/statuses/#{build.sha1}"
|
19
36
|
|
20
|
-
post(path, "pending", build.web_url, "Build ##{build.number} started")
|
37
|
+
post(path, "pending", build.web_url, "Build ##{build.number} started", context(build))
|
21
38
|
end
|
22
39
|
|
23
40
|
# Create a Success or Failure Status for the Commit.
|
@@ -31,11 +48,11 @@ module Janky
|
|
31
48
|
when "failure" then "Build ##{build.number} failed in #{build.duration}s"
|
32
49
|
end
|
33
50
|
|
34
|
-
post(path, status, build.web_url, desc)
|
51
|
+
post(path, status, build.web_url, desc, context(build))
|
35
52
|
end
|
36
53
|
|
37
54
|
# Internal: POST the new status to the API
|
38
|
-
def post(path, status, url, desc)
|
55
|
+
def post(path, status, url, desc, context = nil)
|
39
56
|
http = Net::HTTP.new(@api_url.host, @api_url.port)
|
40
57
|
post = Net::HTTP::Post.new("#{@api_url.path}#{path}")
|
41
58
|
|
@@ -44,11 +61,18 @@ module Janky
|
|
44
61
|
post["Content-Type"] = "application/json"
|
45
62
|
post["Authorization"] = "token #{@token}"
|
46
63
|
|
47
|
-
|
64
|
+
body = {
|
48
65
|
:state => status,
|
49
66
|
:target_url => url,
|
50
67
|
:description => desc,
|
51
|
-
}
|
68
|
+
}
|
69
|
+
|
70
|
+
unless context.nil?
|
71
|
+
post["Accept"] = "application/vnd.github.she-hulk-preview+json"
|
72
|
+
body[:context] = context
|
73
|
+
end
|
74
|
+
|
75
|
+
post.body = body.to_json
|
52
76
|
|
53
77
|
http.request(post)
|
54
78
|
end
|
data/lib/janky/notifier/mock.rb
CHANGED
data/lib/janky/notifier/multi.rb
CHANGED
@@ -6,6 +6,12 @@ module Janky
|
|
6
6
|
@notifiers = notifiers
|
7
7
|
end
|
8
8
|
|
9
|
+
def queued(build)
|
10
|
+
@notifiers.each do |notifier|
|
11
|
+
notifier.queued(build) if notifier.respond_to?(:queued)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
9
15
|
def started(build)
|
10
16
|
@notifiers.each do |notifier|
|
11
17
|
notifier.started(build) if notifier.respond_to?(:started)
|
data/lib/janky/repository.rb
CHANGED
@@ -4,16 +4,19 @@ module Janky
|
|
4
4
|
has_many :commits, :dependent => :destroy
|
5
5
|
has_many :builds, :through => :branches
|
6
6
|
|
7
|
+
after_commit :delete_hook, :on => :destroy
|
8
|
+
|
7
9
|
replicate_associations :builds, :commits, :branches
|
8
10
|
|
9
11
|
default_scope(order("name"))
|
10
12
|
|
11
|
-
def self.setup(nwo, name = nil)
|
13
|
+
def self.setup(nwo, name = nil, template = nil)
|
12
14
|
if nwo.nil?
|
13
15
|
raise ArgumentError, "nwo can't be nil"
|
14
16
|
end
|
15
17
|
|
16
18
|
if repo = Repository.find_by_name(nwo)
|
19
|
+
repo.update_attributes!(:job_template => template)
|
17
20
|
repo.setup
|
18
21
|
return repo
|
19
22
|
end
|
@@ -27,10 +30,10 @@ module Janky
|
|
27
30
|
|
28
31
|
repo =
|
29
32
|
if repo = Repository.find_by_name(name)
|
30
|
-
repo.update_attributes!(:uri => uri)
|
33
|
+
repo.update_attributes!(:uri => uri, :job_template => template)
|
31
34
|
repo
|
32
35
|
else
|
33
|
-
Repository.create!(:name => name, :uri => uri)
|
36
|
+
Repository.create!(:name => name, :uri => uri, :job_template => template)
|
34
37
|
end
|
35
38
|
|
36
39
|
repo.setup
|
@@ -67,12 +70,47 @@ module Janky
|
|
67
70
|
|
68
71
|
# Create or retrieve the given commit.
|
69
72
|
#
|
70
|
-
#
|
73
|
+
# commit - The Hash representation of the Commit.
|
71
74
|
#
|
72
75
|
# Returns a Commit record.
|
73
76
|
def commit_for(commit)
|
74
77
|
commits.find_by_sha1(commit[:sha1]) ||
|
75
|
-
commits.create(commit)
|
78
|
+
commits.create!(commit)
|
79
|
+
end
|
80
|
+
|
81
|
+
def commit_for_sha(sha1)
|
82
|
+
commit_data = GitHub.commit(nwo, sha1)
|
83
|
+
commit_message = commit_data["commit"]["message"]
|
84
|
+
commit_url = github_url("commit/#{sha1}")
|
85
|
+
author_data = commit_data["commit"]["author"]
|
86
|
+
commit_author =
|
87
|
+
if email = author_data["email"]
|
88
|
+
"#{author_data["name"]} <#{email}>"
|
89
|
+
else
|
90
|
+
author_data["name"]
|
91
|
+
end
|
92
|
+
|
93
|
+
commit = commit_for({
|
94
|
+
:repository => self,
|
95
|
+
:sha1 => sha1,
|
96
|
+
:author => commit_author,
|
97
|
+
:message => commit_message,
|
98
|
+
:url => commit_url,
|
99
|
+
})
|
100
|
+
end
|
101
|
+
|
102
|
+
# Create a Janky::Build object given a sha
|
103
|
+
#
|
104
|
+
# sha1 - a string of the target sha to build
|
105
|
+
# user - The login of the GitHub user who pushed.
|
106
|
+
# room_id - optional Fixnum Campfire room ID. Defaults to the room set on
|
107
|
+
# compare - optional String GitHub Compare View URL. Defaults to the
|
108
|
+
#
|
109
|
+
# Returns the newly created Janky::Build
|
110
|
+
def build_sha(sha1, user, room_id = nil, compare = nil)
|
111
|
+
return nil unless sha1 =~ /^[0-9a-fA-F]{7,40}$/
|
112
|
+
commit = commit_for_sha(sha1)
|
113
|
+
commit.build!(user, room_id, compare)
|
76
114
|
end
|
77
115
|
|
78
116
|
# Jenkins host executing this repo's builds.
|
@@ -124,7 +162,7 @@ module Janky
|
|
124
162
|
ChatService.room_name(room_id)
|
125
163
|
end
|
126
164
|
|
127
|
-
# Ditto but returns the
|
165
|
+
# Ditto but returns the String room id. Defaults to the one set
|
128
166
|
# in Campfire.setup.
|
129
167
|
def room_id
|
130
168
|
read_attribute(:room_id) || ChatService.default_room_id
|
@@ -142,9 +180,15 @@ module Janky
|
|
142
180
|
#
|
143
181
|
# Returns nothing.
|
144
182
|
def setup_hook
|
145
|
-
|
146
|
-
|
147
|
-
|
183
|
+
delete_hook
|
184
|
+
|
185
|
+
url = GitHub.hook_create("#{github_owner}/#{github_name}")
|
186
|
+
update_attributes!(:hook_url => url)
|
187
|
+
end
|
188
|
+
|
189
|
+
def delete_hook
|
190
|
+
if self.hook_url? && GitHub.hook_exists?(self.hook_url)
|
191
|
+
GitHub.hook_delete(self.hook_url)
|
148
192
|
end
|
149
193
|
end
|
150
194
|
|
@@ -156,15 +200,19 @@ module Janky
|
|
156
200
|
builder.setup(job_name, uri, job_config_path)
|
157
201
|
end
|
158
202
|
|
159
|
-
# The path of the Jenkins configuration template. Try
|
160
|
-
# first
|
203
|
+
# The path of the Jenkins configuration template. Try
|
204
|
+
# "<job_template>.xml.erb" first, "<repo-name>.xml.erb" second, and then
|
205
|
+
# fallback to "default.xml.erb" under the root config directory.
|
161
206
|
#
|
162
207
|
# Returns the template path as a Pathname.
|
163
208
|
def job_config_path
|
209
|
+
user_override = Janky.jobs_config_dir.join("#{job_template.downcase}.xml.erb") if job_template
|
164
210
|
custom = Janky.jobs_config_dir.join("#{name.downcase}.xml.erb")
|
165
211
|
default = Janky.jobs_config_dir.join("default.xml.erb")
|
166
212
|
|
167
|
-
if
|
213
|
+
if user_override && user_override.readable?
|
214
|
+
user_override
|
215
|
+
elsif custom.readable?
|
168
216
|
custom
|
169
217
|
elsif default.readable?
|
170
218
|
default
|