gitomator-github 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 48039cc1c1db7659a763068cbba64b83eba5db2c
4
+ data.tar.gz: e73d715a625a98e08145314f312fc03dfb3eaab6
5
+ SHA512:
6
+ metadata.gz: ec36dac4420f30b9f940e170df89ca9aea7c0152338151e1deed2cf00c79198f30a4cfd6400ad3fe02357a82799ded98f5a6b27535cfa7e38f91963e8d50d29b
7
+ data.tar.gz: 04359cb8cb41e088ca37afa6db42a214912f2a5af8602d0ffd6b4a0ee22c2037e7bca5fd2fc6d4b8255a933ac1c41cd4781187d2be9e5cc5fa757fec149ad524
data/.gitignore ADDED
@@ -0,0 +1,56 @@
1
+
2
+ # Created by https://www.gitignore.io/api/ruby
3
+
4
+ ### Ruby ###
5
+ *.gem
6
+ *.rbc
7
+ /.config
8
+ /coverage/
9
+ /InstalledFiles
10
+ /pkg/
11
+ /spec/reports/
12
+ /spec/examples.txt
13
+ /test/tmp/
14
+ /test/version_tmp/
15
+ /tmp/
16
+
17
+ # Used by dotenv library to load environment variables.
18
+ # .env
19
+
20
+ ## Specific to RubyMotion:
21
+ .dat*
22
+ .repl_history
23
+ build/
24
+ *.bridgesupport
25
+ build-iPhoneOS/
26
+ build-iPhoneSimulator/
27
+
28
+ ## Specific to RubyMotion (use of CocoaPods):
29
+ #
30
+ # We recommend against adding the Pods directory to your .gitignore. However
31
+ # you should judge for yourself, the pros and cons are mentioned at:
32
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
33
+ #
34
+ # vendor/Pods/
35
+
36
+ ## Documentation cache and generated files:
37
+ /.yardoc/
38
+ /_yardoc/
39
+ /doc/
40
+ /rdoc/
41
+
42
+ ## Environment normalization:
43
+ /.bundle/
44
+ /vendor/bundle
45
+ /lib/bundler/man/
46
+
47
+ # for a library or gem, you might want to ignore these files since the code is
48
+ # intended to run in multiple environments; otherwise, check them in:
49
+ Gemfile.lock
50
+ .ruby-version
51
+ .ruby-gemset
52
+
53
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
54
+ .rvmrc
55
+
56
+ # End of https://www.gitignore.io/api/ruby
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.2.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in gitomator-github.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Joey Freund
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,10 @@
1
+ > **IMPORTANT:** All Gitomator projects are currently in pre-alpha stage, which means that:
2
+ >
3
+ > * Some parts are not implemented
4
+ > * API's may change significantly
5
+ > * There are not a lot of tests
6
+ >
7
+
8
+ # Gitomator::Github
9
+
10
+ See https://gitomator.github.io/
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ begin
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new
6
+ rescue LoadError
7
+ end
8
+
9
+ task :test => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "gitomator/github"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'gitomator/github/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "gitomator-github"
8
+ spec.version = Gitomator::Github::VERSION
9
+ spec.authors = ["Joey Freund"]
10
+ spec.email = ["joeyfreund@gmail.com"]
11
+
12
+ spec.summary = %q{Gitomator GitHub provider}
13
+ spec.description = %q{Automate the management of GitHub organizations}
14
+ spec.homepage = "https://github.com/gitomator/gitomator-github"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "bin"
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.11"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec"
25
+
26
+ spec.add_runtime_dependency 'octokit', '~> 4.2'
27
+ spec.add_runtime_dependency 'gitomator', '>= 0.1.1'
28
+ 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,21 @@
1
+ module Gitomator
2
+ module GitHub
3
+ module Model
4
+ class User
5
+
6
+
7
+ #
8
+ # @param gh_user [Sawyer::Resource]
9
+ #
10
+ def initialize(gh_user)
11
+ @r = gh_user
12
+ end
13
+
14
+ def username
15
+ @r.login
16
+ end
17
+
18
+ end
19
+ end
20
+ end
21
+ 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,5 @@
1
+ module Gitomator
2
+ module Github
3
+ VERSION = "0.1.1"
4
+ end
5
+ 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
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gitomator-github
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Joey Freund
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-12-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: octokit
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: gitomator
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 0.1.1
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 0.1.1
83
+ description: Automate the management of GitHub organizations
84
+ email:
85
+ - joeyfreund@gmail.com
86
+ executables:
87
+ - console
88
+ - setup
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - ".gitignore"
93
+ - ".ruby-version"
94
+ - Gemfile
95
+ - LICENSE.txt
96
+ - README.md
97
+ - Rakefile
98
+ - bin/console
99
+ - bin/setup
100
+ - gitomator-github.gemspec
101
+ - lib/gitomator/github.rb
102
+ - lib/gitomator/github/base_provider.rb
103
+ - lib/gitomator/github/hosting_provider.rb
104
+ - lib/gitomator/github/model/hosted_repo.rb
105
+ - lib/gitomator/github/model/pull_request.rb
106
+ - lib/gitomator/github/model/team.rb
107
+ - lib/gitomator/github/model/user.rb
108
+ - lib/gitomator/github/tagging_provider.rb
109
+ - lib/gitomator/github/version.rb
110
+ homepage: https://github.com/gitomator/gitomator-github
111
+ licenses:
112
+ - MIT
113
+ metadata: {}
114
+ post_install_message:
115
+ rdoc_options: []
116
+ require_paths:
117
+ - lib
118
+ required_ruby_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ requirements: []
129
+ rubyforge_project:
130
+ rubygems_version: 2.6.2
131
+ signing_key:
132
+ specification_version: 4
133
+ summary: Gitomator GitHub provider
134
+ test_files: []