gitomator 0.1.2.2 → 0.1.2.3
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/Gemfile +0 -9
- data/gitomator.gemspec +2 -0
- data/lib/gitomator/github/base_provider.rb +28 -0
- data/lib/gitomator/github/hosting_provider.rb +402 -0
- data/lib/gitomator/github/model/hosted_repo.rb +43 -0
- data/lib/gitomator/github/model/pull_request.rb +81 -0
- data/lib/gitomator/github/model/team.rb +26 -0
- data/lib/gitomator/github/model/user.rb +21 -0
- data/lib/gitomator/github/tagging_provider.rb +95 -0
- data/lib/gitomator/github/version.rb +5 -0
- data/lib/gitomator/github.rb +31 -0
- data/lib/gitomator/travis/ci_provider.rb +109 -0
- data/lib/gitomator/travis/version.rb +5 -0
- data/lib/gitomator/travis.rb +7 -0
- data/lib/gitomator/version.rb +1 -1
- metadata +48 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 72d552670e91c7db9a0850b797cf463ef819bb39
|
4
|
+
data.tar.gz: 51af6b8d334e37b8b05820dc0026731c23330ac4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db58cf86b79ecbb02bc5d248816c08b83b4a6ebd71200803678391243a0fd8856db6773f83e2a1b1e79f9389b4b4261ed20d4f7e0e1ae5b7edc8ef558a47bb37
|
7
|
+
data.tar.gz: 862bb2ff0cf25dd6fe6d6599c7ae012feb87300b572988ebe47057e54d951b4b35bc2be4035ed4ef27710b71d8edeaaf579703410f8fc057ffd10f5e50c60647
|
data/Gemfile
CHANGED
@@ -2,12 +2,3 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in gitomator.gemspec
|
4
4
|
gemspec
|
5
|
-
|
6
|
-
#
|
7
|
-
# FIXME: The whole point is to be able to plug-in different providers (where the
|
8
|
-
# core project is light, and doesn't know/care/depends on any specific provider).
|
9
|
-
# I still don't know of a clean way to do that in Ruby, so for now let's add
|
10
|
-
# gitomator-github and gitomator-travis as dependencies...
|
11
|
-
#
|
12
|
-
gem 'gitomator-github', :git => 'git@github.com:Gitomator/gitomator-github.git'
|
13
|
-
gem 'gitomator-travis', :git => 'git@github.com:Gitomator/gitomator-travis.git'
|
data/gitomator.gemspec
CHANGED
@@ -24,5 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
|
25
25
|
spec.add_runtime_dependency 'trollop', '~> 2.1', '>= 2.1.2'
|
26
26
|
spec.add_runtime_dependency 'logger', '~> 1.2', '>= 1.2.8'
|
27
|
+
spec.add_runtime_dependency 'travis', '~> 1.8', '>= 1.8.2'
|
28
|
+
spec.add_runtime_dependency 'octokit', '~> 4.2'
|
27
29
|
|
28
30
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'gitomator/github'
|
2
|
+
require 'gitomator/util/repo/name_resolver'
|
3
|
+
|
4
|
+
|
5
|
+
module Gitomator
|
6
|
+
module GitHub
|
7
|
+
class BaseProvider
|
8
|
+
|
9
|
+
|
10
|
+
#
|
11
|
+
# @param github_client [Octokit::Client]
|
12
|
+
# @param github_organization [String]
|
13
|
+
#
|
14
|
+
def initialize(github_client, github_organization=nil)
|
15
|
+
@gh = github_client
|
16
|
+
@org = github_organization
|
17
|
+
@repo_name_resolver = Gitomator::Util::Repo::NameResolver.new(@org)
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def name
|
22
|
+
:github
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,402 @@
|
|
1
|
+
require 'gitomator/github'
|
2
|
+
require 'gitomator/github/base_provider'
|
3
|
+
require 'gitomator/github/model/hosted_repo'
|
4
|
+
require 'gitomator/github/model/pull_request'
|
5
|
+
require 'gitomator/github/model/team'
|
6
|
+
require 'gitomator/github/model/user'
|
7
|
+
require 'gitomator/util/repo/name_resolver'
|
8
|
+
|
9
|
+
|
10
|
+
module Gitomator
|
11
|
+
module GitHub
|
12
|
+
class HostingProvider < Gitomator::GitHub::BaseProvider
|
13
|
+
|
14
|
+
#
|
15
|
+
# @param config [Hash<String,Object>]
|
16
|
+
# @return [Gitomator::GitHub::HostingProvider] GitHub hosting provider.
|
17
|
+
#
|
18
|
+
def self.from_config(config = {})
|
19
|
+
org = config['organization'] || config[:organization]
|
20
|
+
return new(Gitomator::GitHub::github_client_from_config(config), org)
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
#=========================================================================
|
25
|
+
|
26
|
+
#
|
27
|
+
# @param github_client [Octokit::Client]
|
28
|
+
# @param github_organization [String]
|
29
|
+
# @param opts [Hash]
|
30
|
+
#
|
31
|
+
def initialize(github_client, github_organization=nil)
|
32
|
+
super
|
33
|
+
# GitHub API doesn't have a straight forward way to get a team by name, so we'll keep an in-memory cache
|
34
|
+
@name2team_cache = {}
|
35
|
+
end
|
36
|
+
|
37
|
+
def name
|
38
|
+
:github
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
# ------------ Helper Methods, Dealing With Naming Conventions -------
|
43
|
+
|
44
|
+
def repo_name_full(repo_name)
|
45
|
+
@repo_name_resolver.full_name(repo_name)
|
46
|
+
end
|
47
|
+
|
48
|
+
#---------------------------------------------------------------------
|
49
|
+
|
50
|
+
def _fetch_teams
|
51
|
+
with_auto_paginate do
|
52
|
+
@name2team_cache = @gh.org_teams(@org).map {|t| [t.name, t]} .to_h
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
#---------------------------- REPO -----------------------------------
|
58
|
+
|
59
|
+
SUPPORTED_CREATE_OPTS = [:description, :homepage, :private, :has_issues,
|
60
|
+
:has_wiki, :has_downloads, :auto_init]
|
61
|
+
#
|
62
|
+
# @option opts [String] :description
|
63
|
+
# @option opts [String] :homepage
|
64
|
+
# @option opts [Boolean] :private
|
65
|
+
# @option opts [Boolean] :has_issues
|
66
|
+
# @option opts [Boolean] :has_wiki
|
67
|
+
# @option opts [Boolean] :has_downloads
|
68
|
+
# @option opts [Boolean] :auto_init
|
69
|
+
#
|
70
|
+
def create_repo(name, opts = {})
|
71
|
+
opts = opts.select {|k,_| SUPPORTED_CREATE_OPTS.include? k }
|
72
|
+
|
73
|
+
# Decide whether this is an organization-repo or a user-repo ...
|
74
|
+
org = @repo_name_resolver.namespace(name)
|
75
|
+
unless org.nil? || org == @gh.user.login
|
76
|
+
opts[:organization] = org
|
77
|
+
end
|
78
|
+
|
79
|
+
return Gitomator::GitHub::Model::HostedRepo.new(
|
80
|
+
@gh.create_repo(@repo_name_resolver.name_only(name), opts)
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def read_repo(name)
|
86
|
+
begin
|
87
|
+
return Gitomator::GitHub::Model::HostedRepo.new(@gh.repo repo_name_full(name))
|
88
|
+
rescue Octokit::NotFound
|
89
|
+
return nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
SUPPORTED_UPDATE_OPTS = [:name, :description, :homepage, :private,
|
95
|
+
:has_issues, :has_wiki, :has_downloads,
|
96
|
+
:default_branch]
|
97
|
+
#
|
98
|
+
# @option opts [String] :name — Name of the repo
|
99
|
+
# @option opts [String] :description — Description of the repo
|
100
|
+
# @option opts [String] :homepage — Home page of the repo
|
101
|
+
# @option opts [Boolean] :private — true makes the repository private, and false makes it public.
|
102
|
+
# @option opts [Boolean] :has_issues — true enables issues for this repo, false disables issues.
|
103
|
+
# @option opts [Boolean] :has_wiki — true enables wiki for this repo, false disables wiki.
|
104
|
+
# @option opts [Boolean] :has_downloads — true enables downloads for this repo, false disables downloads.
|
105
|
+
# @option opts [String] :default_branch — Update the default branch for this repository.
|
106
|
+
#
|
107
|
+
def update_repo(name, opts = {})
|
108
|
+
opts = opts.select {|k,_| SUPPORTED_UPDATE_OPTS.include? k }
|
109
|
+
unless opts.empty?
|
110
|
+
return Gitomator::GitHub::Model::HostedRepo.new(
|
111
|
+
@gh.edit_repository repo_name_full(name), opts)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
def delete_repo(name)
|
117
|
+
@gh.delete_repo repo_name_full(name)
|
118
|
+
end
|
119
|
+
|
120
|
+
#
|
121
|
+
# For opts see http://www.rubydoc.info/gems/octokit/Octokit%2FClient%2FSearch%3Asearch_issues
|
122
|
+
#
|
123
|
+
def search_repos(query, opts = {})
|
124
|
+
gh_repos = nil
|
125
|
+
with_auto_paginate do
|
126
|
+
gh_repos = @gh.search_repos("#{query} user:#{@org}", opts).items
|
127
|
+
end
|
128
|
+
|
129
|
+
gh_repos.map {|r| Gitomator::GitHub::Model::HostedRepo.new(r)}
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
#---------------------------- TEAMS ----------------------------------
|
134
|
+
|
135
|
+
def create_team(name, opts = {})
|
136
|
+
Gitomator::GitHub::Model::Team.new(@gh.create_team(@org, {name: name}))
|
137
|
+
end
|
138
|
+
|
139
|
+
def read_team(name)
|
140
|
+
unless @name2team_cache.has_key? name
|
141
|
+
_fetch_teams()
|
142
|
+
end
|
143
|
+
if @name2team_cache[name]
|
144
|
+
Gitomator::GitHub::Model::Team.new(@name2team_cache[name])
|
145
|
+
else
|
146
|
+
return nil
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
#
|
151
|
+
# opts:
|
152
|
+
# - :name (String)
|
153
|
+
# - :permission (String, one of 'pull', 'push' or 'admin')
|
154
|
+
#
|
155
|
+
def update_team(name, opts)
|
156
|
+
unless @name2team_cache.has_key? name
|
157
|
+
_fetch_teams()
|
158
|
+
end
|
159
|
+
raise "No such team, '#{name}'" unless @name2team_cache.has_key? name
|
160
|
+
|
161
|
+
t = @gh.update_team(@name2team_cache[name].id, opts)
|
162
|
+
@name2team_cache[name] = t
|
163
|
+
return Gitomator::GitHub::Model::Team.new(t)
|
164
|
+
end
|
165
|
+
|
166
|
+
def delete_team(name)
|
167
|
+
unless @name2team_cache.has_key? name
|
168
|
+
_fetch_teams()
|
169
|
+
end
|
170
|
+
if @name2team_cache.has_key? name
|
171
|
+
@gh.delete_team @name2team_cache[name].id
|
172
|
+
@name2team_cache.delete(name)
|
173
|
+
return true
|
174
|
+
end
|
175
|
+
return false
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
def search_teams(query, opts={})
|
180
|
+
result = @name2team_cache.select {|k,_| k.downcase.include? query} .values
|
181
|
+
if result.empty?
|
182
|
+
_fetch_teams()
|
183
|
+
result = @name2team_cache.select {|k,_| k.downcase.include? query} .values
|
184
|
+
end
|
185
|
+
return result.map {|t| Gitomator::GitHub::Model::Team.new(t)}
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
#---------------------------------------------------------------------
|
190
|
+
|
191
|
+
def set_user_permission(user, repo, permission)
|
192
|
+
permission = _strinigify_permission(permission)
|
193
|
+
if permission.nil?
|
194
|
+
@gh.remove_collab(repo_name_full(repo), user)
|
195
|
+
else
|
196
|
+
@gh.add_collab(repo_name_full(repo), user, {permission: permission})
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
def set_team_permission(team, repo, permission)
|
202
|
+
permission = _strinigify_permission(permission)
|
203
|
+
|
204
|
+
t = read_team(team)
|
205
|
+
raise "No such team, #{team}" if t.nil?
|
206
|
+
|
207
|
+
if permission.nil?
|
208
|
+
@gh.remove_team_repo(t.id, repo_name_full(repo))
|
209
|
+
else
|
210
|
+
@gh.add_team_repo(t.id, repo_name_full(repo),
|
211
|
+
{
|
212
|
+
permission: permission,
|
213
|
+
accept: 'application/vnd.github.ironman-preview+json'
|
214
|
+
}
|
215
|
+
)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
def _strinigify_permission(permission)
|
221
|
+
if permission.nil?
|
222
|
+
return nil
|
223
|
+
end
|
224
|
+
|
225
|
+
case permission.to_s
|
226
|
+
when 'read' || 'pull'
|
227
|
+
return 'pull'
|
228
|
+
when 'write' || 'push'
|
229
|
+
return 'push'
|
230
|
+
else
|
231
|
+
raise "Invalid permission '#{permission}'"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
|
237
|
+
#--------------------------- Team Membership -------------------------
|
238
|
+
|
239
|
+
#----------- Helpers ---------
|
240
|
+
|
241
|
+
def gitomator_role_2_github_role(role)
|
242
|
+
if ['admin', 'maintainer'].include? role.to_s.downcase
|
243
|
+
return 'maintainer'
|
244
|
+
else
|
245
|
+
return 'member'
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def github_role_2_gitomator_role(role)
|
250
|
+
role == 'maintainer' ? 'admin' : role
|
251
|
+
end
|
252
|
+
|
253
|
+
def team_id(team_name)
|
254
|
+
team = read_team(team_name)
|
255
|
+
raise "No such team, #{team_name}" if team.nil?
|
256
|
+
return team.id
|
257
|
+
end
|
258
|
+
|
259
|
+
#-----------------------------
|
260
|
+
|
261
|
+
|
262
|
+
def create_team_membership(team_name, user_name, role='member')
|
263
|
+
@gh.add_team_membership(team_id(team_name), user_name,
|
264
|
+
{ :role => gitomator_role_2_github_role(role) }
|
265
|
+
)
|
266
|
+
return role
|
267
|
+
end
|
268
|
+
|
269
|
+
|
270
|
+
def read_team_membership(team_name, user_name)
|
271
|
+
begin
|
272
|
+
m = @gh.team_membership(team_id(team_name), user_name)
|
273
|
+
return m.nil? ? nil : m.role
|
274
|
+
rescue Octokit::NotFound
|
275
|
+
return nil
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
|
280
|
+
def update_team_membership(team_name, user_name, role)
|
281
|
+
@gh.add_team_membership(team_id(team_name), user_name,
|
282
|
+
{ :role => gitomator_role_2_github_role(role) } )
|
283
|
+
return role
|
284
|
+
end
|
285
|
+
|
286
|
+
|
287
|
+
def delete_team_membership(team_name, user_name)
|
288
|
+
@gh.remove_team_membership(team_id(team_name), user_name)
|
289
|
+
end
|
290
|
+
|
291
|
+
|
292
|
+
#---------------------------------------------------------------------
|
293
|
+
|
294
|
+
def with_auto_paginate
|
295
|
+
raise "You must supply a block" unless block_given?
|
296
|
+
begin
|
297
|
+
@gh.auto_paginate = true # We want to get all team members
|
298
|
+
yield
|
299
|
+
ensure
|
300
|
+
@gh.auto_paginate = nil # We don't want to hit GitHub's API rate-limit
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
|
305
|
+
def search_users(query, opts={})
|
306
|
+
|
307
|
+
# If the team's name is specified ...
|
308
|
+
gh_users = nil
|
309
|
+
if opts[:team_name]
|
310
|
+
team = read_team(opts[:team_name])
|
311
|
+
gh_users = with_auto_paginate { @gh.team_members(team.id) }
|
312
|
+
else
|
313
|
+
gh_users = with_auto_paginate { @gh.org_members(@org) }
|
314
|
+
end
|
315
|
+
|
316
|
+
result = gh_users.map { |u| Gitomator::GitHub::Model::User.new(u) }
|
317
|
+
|
318
|
+
if query.is_a?(String) && (! query.empty?)
|
319
|
+
result = result.select {|u| u.username.include? query }
|
320
|
+
elsif query.is_a? Regexp
|
321
|
+
result = result.select {|u| query.match(u.username) }
|
322
|
+
end
|
323
|
+
|
324
|
+
return result
|
325
|
+
end
|
326
|
+
|
327
|
+
|
328
|
+
#---------------------------------------------------------------------
|
329
|
+
|
330
|
+
|
331
|
+
#
|
332
|
+
# @param src (String) of the following format 'org/repo:branch'.
|
333
|
+
# @param dst (String) of the following format 'org/repo:branch'.
|
334
|
+
#
|
335
|
+
def create_pull_request(src, dst, opts = {})
|
336
|
+
|
337
|
+
def extract_org_repo_and_branch(src_or_dst)
|
338
|
+
match = src_or_dst.match(/(.+)\/(.+):(.+)/i)
|
339
|
+
raise "Invalid src/dst, #{src_or_dst} (expected: `org_or_user/repo:branch`)" if match.nil?
|
340
|
+
return match.captures
|
341
|
+
end
|
342
|
+
|
343
|
+
src_org, src_repo, src_branch = extract_org_repo_and_branch(src)
|
344
|
+
dst_org, dst_repo, dst_branch = extract_org_repo_and_branch(dst)
|
345
|
+
|
346
|
+
unless src_repo == dst_repo
|
347
|
+
raise "Cannot create pull-request from #{src} to #{dst} (must be the same repo or a fork)."
|
348
|
+
end
|
349
|
+
|
350
|
+
Gitomator::GitHub::Model::PullRequest.new(
|
351
|
+
@gh.create_pull_request("#{dst_org}/#{dst_repo}", dst_branch,
|
352
|
+
(src_org == dst_org ? '' : "#{src_org}:") + src_branch,
|
353
|
+
opts[:title] || 'New Pull Request',
|
354
|
+
opts[:body] || 'Pull-request created using Gitomator.'
|
355
|
+
)
|
356
|
+
)
|
357
|
+
end
|
358
|
+
|
359
|
+
|
360
|
+
def read_pull_request(dst_repo, id)
|
361
|
+
begin
|
362
|
+
return Gitomator::GitHub::Model::PullRequest.new(
|
363
|
+
@gh.pull_request(repo_name_full(dst_repo), id))
|
364
|
+
rescue Octokit::NotFound
|
365
|
+
return nil
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
|
370
|
+
#
|
371
|
+
# @param opts [Hash]
|
372
|
+
# => @param :state [Symbol] One of :open, :close or :all (default: :open)
|
373
|
+
#
|
374
|
+
def read_pull_requests(dst_repo, opts = {})
|
375
|
+
@gh.pulls(repo_name_full(dst_repo), opts)
|
376
|
+
.map {|pr| Gitomator::GitHub::Model::PullRequest.new(pr, @gh)}
|
377
|
+
end
|
378
|
+
|
379
|
+
|
380
|
+
def merge_pull_request(dst_repo, id, message='')
|
381
|
+
Gitomator::GitHub::Model::PullRequest.new(
|
382
|
+
@gh.merge_pull_request(repo_name_full(dst_repo), id, message), @gh)
|
383
|
+
end
|
384
|
+
|
385
|
+
def close_pull_request(dst_repo, id)
|
386
|
+
Gitomator::GitHub::Model::PullRequest.new(
|
387
|
+
@gh.close_pull_request(repo_name_full(dst_repo), id), @gh)
|
388
|
+
end
|
389
|
+
|
390
|
+
def open_pull_request(dst_repo, id)
|
391
|
+
Gitomator::GitHub::Model::PullRequest.new(
|
392
|
+
@gh.update_pull_request(repo_name_full(dst_repo), id, {state: :open}), @gh)
|
393
|
+
end
|
394
|
+
|
395
|
+
|
396
|
+
#---------------------------------------------------------------------
|
397
|
+
|
398
|
+
|
399
|
+
|
400
|
+
end
|
401
|
+
end
|
402
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Gitomator
|
2
|
+
module GitHub
|
3
|
+
module Model
|
4
|
+
class HostedRepo
|
5
|
+
|
6
|
+
|
7
|
+
#
|
8
|
+
# @param gh_repo [Sawyer::Resource]
|
9
|
+
#
|
10
|
+
def initialize(gh_repo)
|
11
|
+
@r = gh_repo
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
def name
|
16
|
+
@r.name
|
17
|
+
end
|
18
|
+
|
19
|
+
def full_name
|
20
|
+
@r.full_name
|
21
|
+
end
|
22
|
+
|
23
|
+
def url
|
24
|
+
@r.clone_url
|
25
|
+
end
|
26
|
+
|
27
|
+
def properties
|
28
|
+
return {
|
29
|
+
:description => @r.description,
|
30
|
+
:homepage => @r.homepage,
|
31
|
+
:private => @r.private?,
|
32
|
+
:has_issues => @r.has_issues?,
|
33
|
+
:has_wiki => @r.has_wiki?,
|
34
|
+
:has_downloads => @r.has_downloads?,
|
35
|
+
:default_branch => @r.default_branch
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'gitomator/github/model/hosted_repo'
|
2
|
+
|
3
|
+
module Gitomator
|
4
|
+
module GitHub
|
5
|
+
module Model
|
6
|
+
class PullRequest
|
7
|
+
|
8
|
+
|
9
|
+
#
|
10
|
+
# @param gh_pull_request [Sawyer::Resource]
|
11
|
+
# @param octokit [Octokit::Client]
|
12
|
+
#
|
13
|
+
def initialize(gh_pull_request, octokit)
|
14
|
+
@r = gh_pull_request
|
15
|
+
@octokit = octokit
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def id
|
20
|
+
@r.number
|
21
|
+
end
|
22
|
+
|
23
|
+
def title
|
24
|
+
@r.title
|
25
|
+
end
|
26
|
+
|
27
|
+
def created_by
|
28
|
+
@r.user.login
|
29
|
+
end
|
30
|
+
|
31
|
+
def created_at
|
32
|
+
@r.created_at
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [String] One of 'open', 'close'
|
36
|
+
#
|
37
|
+
def state
|
38
|
+
@r.state
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# @return true/false/nil
|
43
|
+
#
|
44
|
+
def mergeable?
|
45
|
+
# In Octokit, the two methods `pull_request` and `pull_requests` return
|
46
|
+
# different type of objects (the one returned by `pull_requests` is missing
|
47
|
+
# the mergeable? method)
|
48
|
+
if (not @r.respond_to? :mergeable?)
|
49
|
+
@r = @octokit.pull_request(@r.base.repo.full_name, @r.number)
|
50
|
+
end
|
51
|
+
|
52
|
+
if @r.mergeable_state == 'clean'
|
53
|
+
return @r.mergeable?
|
54
|
+
else
|
55
|
+
return nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# The "source repo"
|
60
|
+
def head_repo
|
61
|
+
Gitomator::GitHub::Model::HostedRepo.new(@r.head.repo)
|
62
|
+
end
|
63
|
+
|
64
|
+
def head_branch
|
65
|
+
@r.head.label.split(':').last
|
66
|
+
end
|
67
|
+
|
68
|
+
# The "destination repo"
|
69
|
+
def base_repo
|
70
|
+
Gitomator::GitHub::Model::HostedRepo.new(@r.base.repo)
|
71
|
+
end
|
72
|
+
|
73
|
+
def base_branch
|
74
|
+
@r.base.label.split(':').last
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Gitomator
|
2
|
+
module GitHub
|
3
|
+
module Model
|
4
|
+
class Team
|
5
|
+
|
6
|
+
|
7
|
+
#
|
8
|
+
# @param gh_team [Sawyer::Resource]
|
9
|
+
#
|
10
|
+
def initialize(gh_team)
|
11
|
+
@r = gh_team
|
12
|
+
end
|
13
|
+
|
14
|
+
def id
|
15
|
+
@r.id
|
16
|
+
end
|
17
|
+
|
18
|
+
def name
|
19
|
+
@r.name
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'gitomator/github'
|
2
|
+
require 'gitomator/github/base_provider'
|
3
|
+
|
4
|
+
|
5
|
+
module Gitomator
|
6
|
+
module GitHub
|
7
|
+
class TaggingProvider < Gitomator::GitHub::BaseProvider
|
8
|
+
|
9
|
+
|
10
|
+
# ---------------------- Static Factory Methods --------------------------
|
11
|
+
|
12
|
+
#
|
13
|
+
# @param config [Hash<String,Object>]
|
14
|
+
# @return [Gitomator::GitHub::HostingProvider] GitHub hosting provider.
|
15
|
+
#
|
16
|
+
def self.from_config(config = {})
|
17
|
+
return new(Gitomator::GitHub::github_client_from_config(config),
|
18
|
+
config['organization'])
|
19
|
+
end
|
20
|
+
|
21
|
+
#-------------------------------------------------------------------------
|
22
|
+
|
23
|
+
|
24
|
+
def add_tags(repo, issue_or_pr_id, *tags)
|
25
|
+
@gh.add_labels_to_an_issue(@repo_name_resolver.full_name(repo),
|
26
|
+
issue_or_pr_id, tags
|
27
|
+
).map { |r| r.to_h }
|
28
|
+
end
|
29
|
+
|
30
|
+
def remove_tag(repo, id_or_name, tag)
|
31
|
+
@gh.remove_label(@repo_name_resolver.full_name(repo), id_or_name, tag)
|
32
|
+
.map { |r| r.to_h } # Make the result a regular Ruby Hash
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def tags(repo, id_or_name)
|
37
|
+
repo = @repo_name_resolver.full_name(repo)
|
38
|
+
@gh.labels_for_issue(repo, id_or_name).map {|r| r.name }
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
#
|
43
|
+
# @return Enumerable of object identifiers.
|
44
|
+
#
|
45
|
+
def search(repo, label)
|
46
|
+
if label.is_a? String
|
47
|
+
q = "repo:#{@repo_name_resolver.full_name(repo)} type:issue|pr label:\"#{label}\""
|
48
|
+
@gh.search_issues(q)
|
49
|
+
.items.map {|item| item.number} # Make the result an array of issue/or id's
|
50
|
+
else
|
51
|
+
raise "Unimplemented! Search only supports a single tag at the moment."
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def metadata(repo, tag=nil)
|
58
|
+
repo = @repo_name_resolver.full_name(repo)
|
59
|
+
|
60
|
+
if tag
|
61
|
+
begin
|
62
|
+
@gh.label(repo, tag).to_h # Return metadata (Hash<Symbol,String>)
|
63
|
+
rescue Octokit::NotFound
|
64
|
+
return nil
|
65
|
+
end
|
66
|
+
else
|
67
|
+
@gh.labels(repo).map {|r| [r.name, r.to_h]}.to_h # Return Hash<String,Hash<Symbol,String>>, mapping tags to their metadata
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
def set_metadata(repo, tag, metadata)
|
74
|
+
repo = @repo_name_resolver.full_name(repo)
|
75
|
+
color = metadata[:color] || metadata['color']
|
76
|
+
raise "The only supported metadata property is 'color'" if color.nil?
|
77
|
+
# TODO: Validate the color string (6-char-long Hex string. Any other formats supproted by GitHub?)
|
78
|
+
|
79
|
+
if metadata(repo, tag).nil?
|
80
|
+
@gh.add_label(repo, tag, color).to_h
|
81
|
+
else
|
82
|
+
@gh.update_label(repo, tag, {:color => color}).to_h
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
def delete_metadata(repo, tag)
|
89
|
+
@gh.delete_label!(@repo_name_resolver.full_name(repo), tag)
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "gitomator/github/version"
|
2
|
+
|
3
|
+
module Gitomator
|
4
|
+
module GitHub
|
5
|
+
|
6
|
+
|
7
|
+
def self.github_client_from_config(config = {})
|
8
|
+
# Convert keys yo strings (to handle keys whose type is Symbol)
|
9
|
+
config = config.map {|k,v| [k.to_s, v]} .to_h
|
10
|
+
|
11
|
+
opts = {}
|
12
|
+
|
13
|
+
if config['access_token']
|
14
|
+
opts[:access_token] = config['access_token']
|
15
|
+
elsif config['username'] && config['password']
|
16
|
+
opts[:login] = config['username']
|
17
|
+
opts[:password] = config['password']
|
18
|
+
elsif config['client_id'] && config['client_secret']
|
19
|
+
opts[:client_id] = config['client_id']
|
20
|
+
opts[:client_secret] = config['client_secret']
|
21
|
+
else
|
22
|
+
raise "Invalid GitHub hosting configuration - #{config}"
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'octokit'
|
26
|
+
return Octokit::Client.new(opts)
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'gitomator/util/repo/name_resolver'
|
2
|
+
require 'travis'
|
3
|
+
|
4
|
+
module Gitomator
|
5
|
+
module Travis
|
6
|
+
class CIProvider
|
7
|
+
|
8
|
+
|
9
|
+
# ---------------------- Static Factory Methods --------------------------
|
10
|
+
|
11
|
+
class << self
|
12
|
+
private :new
|
13
|
+
end
|
14
|
+
|
15
|
+
#
|
16
|
+
# @param config [Hash<String,Object>]
|
17
|
+
# @return [Gitomator::GitHub::HostingProvider] GitHub hosting provider.
|
18
|
+
#
|
19
|
+
def self.from_config(config = {})
|
20
|
+
config = config.map {|k,v| [k.to_s, v] } .to_h
|
21
|
+
|
22
|
+
if config['provider'] == 'travis'
|
23
|
+
uri = ::Travis::Client::ORG_URI
|
24
|
+
elsif config['provider'] == 'travis_pro'
|
25
|
+
uri = ::Travis::Client::PRO_URI
|
26
|
+
else
|
27
|
+
raise "Invalid Travis CI provider name, #{config['provider']}."
|
28
|
+
end
|
29
|
+
|
30
|
+
if config['access_token']
|
31
|
+
access_token = config['access_token']
|
32
|
+
elsif config['github_access_token']
|
33
|
+
access_token = ::Travis.github_auth(config['github_access_token'])
|
34
|
+
else
|
35
|
+
raise "Invalid Travis CI provider config - #{config}"
|
36
|
+
end
|
37
|
+
|
38
|
+
travis_client = ::Travis::Client.new({:uri => uri, :access_token => access_token })
|
39
|
+
return new(travis_client, config['github_organization'])
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
# ------------------------------------------------------------------------
|
44
|
+
|
45
|
+
|
46
|
+
#
|
47
|
+
# @param travis_client [Travis::Client::Session]
|
48
|
+
# @param github_organization [String] - Default GitHub organization
|
49
|
+
#
|
50
|
+
def initialize(travis_client, github_organization, opts={})
|
51
|
+
raise "Travis client is nil" if travis_client.nil?
|
52
|
+
@travis = travis_client
|
53
|
+
@org = github_organization
|
54
|
+
@repo_name_resolver = Gitomator::Util::Repo::NameResolver.new(@org)
|
55
|
+
end
|
56
|
+
|
57
|
+
def name
|
58
|
+
:travis
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def _find_repo_and_execute_block(repo)
|
63
|
+
begin
|
64
|
+
yield @travis.repo(@repo_name_resolver.full_name(repo))
|
65
|
+
rescue ::Travis::Client::NotFound
|
66
|
+
return nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
def enable_ci(repo, opts={})
|
72
|
+
_find_repo_and_execute_block(repo) {|r| r.enable}
|
73
|
+
end
|
74
|
+
|
75
|
+
def disable_ci(repo, opts={})
|
76
|
+
_find_repo_and_execute_block(repo) {|r| r.disable}
|
77
|
+
end
|
78
|
+
|
79
|
+
def ci_enabled?(repo)
|
80
|
+
_find_repo_and_execute_block(repo) {|r| r.reload.active? }
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
#
|
86
|
+
# @param blocking [Boolean]
|
87
|
+
#
|
88
|
+
def sync(blocking=false, opts={})
|
89
|
+
@travis.user.sync()
|
90
|
+
while blocking && syncing?
|
91
|
+
sleep(1)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
#
|
97
|
+
# @return Boolean - Indicates whether a sync' is currently in progress.
|
98
|
+
#
|
99
|
+
def syncing?(opts={})
|
100
|
+
@travis.user.reload.syncing?
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/lib/gitomator/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitomator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.2.
|
4
|
+
version: 0.1.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joey Freund
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -92,6 +92,40 @@ dependencies:
|
|
92
92
|
- - ">="
|
93
93
|
- !ruby/object:Gem::Version
|
94
94
|
version: 1.2.8
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: travis
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - "~>"
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '1.8'
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: 1.8.2
|
105
|
+
type: :runtime
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - "~>"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '1.8'
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: 1.8.2
|
115
|
+
- !ruby/object:Gem::Dependency
|
116
|
+
name: octokit
|
117
|
+
requirement: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - "~>"
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '4.2'
|
122
|
+
type: :runtime
|
123
|
+
prerelease: false
|
124
|
+
version_requirements: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - "~>"
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '4.2'
|
95
129
|
description:
|
96
130
|
email:
|
97
131
|
- joeyfreund@gmail.com
|
@@ -127,6 +161,15 @@ files:
|
|
127
161
|
- lib/gitomator/console.rb
|
128
162
|
- lib/gitomator/context.rb
|
129
163
|
- lib/gitomator/exceptions.rb
|
164
|
+
- lib/gitomator/github.rb
|
165
|
+
- lib/gitomator/github/base_provider.rb
|
166
|
+
- lib/gitomator/github/hosting_provider.rb
|
167
|
+
- lib/gitomator/github/model/hosted_repo.rb
|
168
|
+
- lib/gitomator/github/model/pull_request.rb
|
169
|
+
- lib/gitomator/github/model/team.rb
|
170
|
+
- lib/gitomator/github/model/user.rb
|
171
|
+
- lib/gitomator/github/tagging_provider.rb
|
172
|
+
- lib/gitomator/github/version.rb
|
130
173
|
- lib/gitomator/service.rb
|
131
174
|
- lib/gitomator/service/ci.rb
|
132
175
|
- lib/gitomator/service/git.rb
|
@@ -143,6 +186,9 @@ files:
|
|
143
186
|
- lib/gitomator/task/make_repos.rb
|
144
187
|
- lib/gitomator/task/setup_team.rb
|
145
188
|
- lib/gitomator/task/update_repo_access_permissions.rb
|
189
|
+
- lib/gitomator/travis.rb
|
190
|
+
- lib/gitomator/travis/ci_provider.rb
|
191
|
+
- lib/gitomator/travis/version.rb
|
146
192
|
- lib/gitomator/util/repo/name_resolver.rb
|
147
193
|
- lib/gitomator/util/script_util.rb
|
148
194
|
- lib/gitomator/version.rb
|