codeowners 0.0.2 → 0.0.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/.gitignore +1 -0
- data/CHANGELOG.md +5 -0
- data/codeowners.gemspec +1 -0
- data/lib/codeowners.rb +4 -0
- data/lib/codeowners/cli.rb +95 -0
- data/lib/codeowners/guess.rb +77 -0
- data/lib/codeowners/import/client.rb +136 -0
- data/lib/codeowners/import/organization.rb +32 -0
- data/lib/codeowners/list_contributors.rb +4 -0
- data/lib/codeowners/result.rb +4 -0
- data/lib/codeowners/storage.rb +79 -0
- data/lib/codeowners/storage/collection.rb +37 -0
- data/lib/codeowners/storage/data.rb +30 -0
- data/lib/codeowners/version.rb +1 -1
- metadata +22 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 790787fbbbface03dca93e7ee9fb70818d736b55b5b46feba832bb5d1d1cacde
|
4
|
+
data.tar.gz: 5cf3d0d5b9612527ac8e3d5416442dcb0442e1d69180b85df1b25752871e9d9e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9728df04569e092a74db19feec631cb0f4031da24ab1bf7e127b6a728206c21026fc7a2988ff0c81f66b894a895350521a9f956236859e682eaa8be2f79194a3
|
7
|
+
data.tar.gz: 594590f13fe02ea9c8e7f5831e2d716a48779e7309f063caeb194c884a42d69037fed8ca64b6703ad88db7765f25258fa58d9d45e12b3eabb00875a016b1fb3a
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
# Codeowners
|
2
2
|
Simple CLI to interact with GitHub CODEOWNERS
|
3
3
|
|
4
|
+
## v0.0.3 - 2020-07-22
|
5
|
+
### Added
|
6
|
+
- [Luca Guidi] Added `codeowners guess`
|
7
|
+
- [Luca Guidi] Added `codeowners import org`
|
8
|
+
|
4
9
|
## v0.0.2 - 2020-06-27
|
5
10
|
### Added
|
6
11
|
- [Luca Guidi] Added pattern support to `codeowners contributors`
|
data/codeowners.gemspec
CHANGED
@@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.require_paths = ["lib"]
|
28
28
|
|
29
29
|
spec.add_runtime_dependency "dry-cli", "~> 0.6"
|
30
|
+
spec.add_runtime_dependency "excon", "~> 0.75"
|
30
31
|
spec.add_development_dependency "rubocop"
|
31
32
|
spec.add_development_dependency "byebug"
|
32
33
|
end
|
data/lib/codeowners.rb
CHANGED
@@ -3,8 +3,12 @@
|
|
3
3
|
module Codeowners
|
4
4
|
require "codeowners/version"
|
5
5
|
require "codeowners/result"
|
6
|
+
require "codeowners/storage"
|
6
7
|
require "codeowners/list_owners"
|
7
8
|
require "codeowners/list_contributors"
|
9
|
+
require "codeowners/guess"
|
10
|
+
require "codeowners/import/client"
|
11
|
+
require "codeowners/import/organization"
|
8
12
|
|
9
13
|
class Error < StandardError
|
10
14
|
end
|
data/lib/codeowners/cli.rb
CHANGED
@@ -95,9 +95,104 @@ module Codeowners
|
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
98
|
+
class Guess < Command
|
99
|
+
DEFAULT_BASE_DIRECTORY = Dir.pwd.dup.freeze
|
100
|
+
private_constant :DEFAULT_BASE_DIRECTORY
|
101
|
+
|
102
|
+
DEFAULT_CODEOWNERS_PATH = ::File.join(".github", "CODEOWNERS").freeze
|
103
|
+
private_constant :DEFAULT_CODEOWNERS_PATH
|
104
|
+
|
105
|
+
FORMAT_MAPPING = { "string" => "to_s", "csv" => "to_csv" }.freeze
|
106
|
+
private_constant :FORMAT_MAPPING
|
107
|
+
|
108
|
+
FORMAT_VALUES = FORMAT_MAPPING.keys.freeze
|
109
|
+
private_constant :FORMAT_VALUES
|
110
|
+
|
111
|
+
DEFAULT_FORMAT = FORMAT_VALUES.first
|
112
|
+
private_constant :DEFAULT_FORMAT
|
113
|
+
|
114
|
+
DEFAULT_STORAGE_PATH = ::File.join(Dir.pwd, "codeowners.json").freeze
|
115
|
+
private_constant :DEFAULT_STORAGE_PATH
|
116
|
+
|
117
|
+
DEFAULT_DEBUG = false
|
118
|
+
private_constant :DEFAULT_DEBUG
|
119
|
+
|
120
|
+
desc "Check CODEOWNERS for a file (or a pattern), if missing, it tries to guess, by looking at the git commit history"
|
121
|
+
|
122
|
+
argument :file, required: true, desc: "File (or pattern) to check"
|
123
|
+
|
124
|
+
option :base_directory, type: :string, default: DEFAULT_BASE_DIRECTORY, desc: "Base directory"
|
125
|
+
option :codeowners, type: :string, default: DEFAULT_CODEOWNERS_PATH, desc: "Path to CODEOWNERS file"
|
126
|
+
option :storage, type: :string, default: DEFAULT_STORAGE_PATH, desc: "Storage path (default: #{DEFAULT_STORAGE_PATH})"
|
127
|
+
|
128
|
+
option :format, type: :string, default: DEFAULT_FORMAT, values: FORMAT_VALUES, desc: "Output format"
|
129
|
+
option :debug, type: :boolean, default: DEFAULT_DEBUG, desc: "Print debug information to stdout"
|
130
|
+
|
131
|
+
example [
|
132
|
+
"path/to/file.rb # file",
|
133
|
+
"'path/to/**/*.rb' # pattern"
|
134
|
+
]
|
135
|
+
|
136
|
+
def call(file:, base_directory:, codeowners:, storage:, format:, debug:, **)
|
137
|
+
owners = Codeowners::ListOwners.new(base_directory, codeowners)
|
138
|
+
contributors = Codeowners::ListContributors.new(base_directory)
|
139
|
+
storage = Codeowners::Storage.new(storage)
|
140
|
+
|
141
|
+
result = Codeowners::Guess.new(owners, contributors, storage, base_directory, out).call(file, debug)
|
142
|
+
# exit(1) unless result.successful?
|
143
|
+
|
144
|
+
out.puts output(result, format)
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def output(result, format)
|
150
|
+
# method_name = FORMAT_MAPPING.fetch(format)
|
151
|
+
return unless format == "csv"
|
152
|
+
|
153
|
+
result.map do |file, data|
|
154
|
+
"#{file},#{data.fetch(:teams).join('/')},#{data.fetch(:codeowners)}"
|
155
|
+
end.join("\n")
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
module Import
|
160
|
+
class Org < Command
|
161
|
+
DEFAULT_STORAGE_PATH = ::File.join(Dir.pwd, "codeowners.json").freeze
|
162
|
+
private_constant :DEFAULT_STORAGE_PATH
|
163
|
+
|
164
|
+
DEFAULT_DEBUG = false
|
165
|
+
private_constant :DEFAULT_DEBUG
|
166
|
+
|
167
|
+
desc "Import teams and members for a GitHub organization"
|
168
|
+
|
169
|
+
argument :org, required: true, desc: "GitHub organization login"
|
170
|
+
argument :token, required: true, desc: "GitHub APIv3 token"
|
171
|
+
|
172
|
+
option :storage, type: :string, default: DEFAULT_STORAGE_PATH, desc: "Storage path (default: #{DEFAULT_STORAGE_PATH})"
|
173
|
+
option :debug, type: :boolean, default: DEFAULT_DEBUG, desc: "Print debug information to stdout"
|
174
|
+
|
175
|
+
example [
|
176
|
+
"hanami s3cr374p1t0k3n"
|
177
|
+
]
|
178
|
+
|
179
|
+
def call(org:, token:, storage:, debug:, **)
|
180
|
+
client = Codeowners::Import::Client.new(token, out)
|
181
|
+
storage = Codeowners::Storage.new(storage)
|
182
|
+
|
183
|
+
Codeowners::Import::Organization.new(client, storage).call(org, debug)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
98
188
|
register "version", Version, aliases: ["v", "-v", "--version"]
|
99
189
|
register "list", List
|
100
190
|
register "contributors", Contributors
|
191
|
+
register "guess", Guess
|
192
|
+
|
193
|
+
register "import" do |prefix|
|
194
|
+
prefix.register "org", Import::Org
|
195
|
+
end
|
101
196
|
end
|
102
197
|
end
|
103
198
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Codeowners
|
4
|
+
class Guess
|
5
|
+
def initialize(owners, contributors, storage, base_directory, out)
|
6
|
+
@owners = owners
|
7
|
+
@contributors = contributors
|
8
|
+
@storage = storage
|
9
|
+
@base_directory = ::File.expand_path(base_directory)
|
10
|
+
@out = out
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(file, debug)
|
14
|
+
result = {}
|
15
|
+
|
16
|
+
Dir.chdir(base_directory) do
|
17
|
+
Dir.glob(file).sort.each do |f|
|
18
|
+
*teams, codeowners = list_code_owners(f, debug)
|
19
|
+
*teams, codeowners = guess_code_owners(f, debug) unless codeowners
|
20
|
+
teams ||= []
|
21
|
+
|
22
|
+
result[f] = { teams: teams, codeowners: codeowners }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
result
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_reader :owners
|
32
|
+
attr_reader :contributors
|
33
|
+
attr_reader :storage
|
34
|
+
attr_reader :base_directory
|
35
|
+
attr_reader :out
|
36
|
+
|
37
|
+
def list_code_owners(file, _debug)
|
38
|
+
result = owners.call(file)
|
39
|
+
|
40
|
+
if result.successful?
|
41
|
+
teams = result.to_a.find_all do |team|
|
42
|
+
storage.team_exist?(team) &&
|
43
|
+
!storage.blacklisted_team?(team)
|
44
|
+
end
|
45
|
+
|
46
|
+
return [result, true] if teams.any?
|
47
|
+
end
|
48
|
+
|
49
|
+
[nil, false]
|
50
|
+
end
|
51
|
+
|
52
|
+
def guess_code_owners(file, debug)
|
53
|
+
result = contributors.call(file, debug)
|
54
|
+
return [nil, false] unless result.successful?
|
55
|
+
|
56
|
+
contributors = result.to_a
|
57
|
+
result = contributors.each_with_object({}) do |contributor, memo|
|
58
|
+
teams = storage.teams_for(contributor)
|
59
|
+
teams.each do |team|
|
60
|
+
slug = team.fetch("slug")
|
61
|
+
|
62
|
+
memo[slug] ||= {}
|
63
|
+
memo[slug][:insertions] ||= 0
|
64
|
+
memo[slug][:deletions] ||= 0
|
65
|
+
memo[slug][:insertions] += contributor.insertions
|
66
|
+
memo[slug][:deletions] += contributor.deletions
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
team = result.sort do |a, b|
|
71
|
+
-a.last.fetch(:insertions) <=> -b.last.fetch(:insertions)
|
72
|
+
end&.first&.first
|
73
|
+
|
74
|
+
[team, false]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "excon"
|
5
|
+
|
6
|
+
module Codeowners
|
7
|
+
module Import
|
8
|
+
class Client
|
9
|
+
BASE_URL = "https://api.github.com"
|
10
|
+
private_constant :BASE_URL
|
11
|
+
|
12
|
+
USER_AGENT = "codeowners v#{Codeowners::VERSION}"
|
13
|
+
private_constant :USER_AGENT
|
14
|
+
|
15
|
+
def initialize(token, out, base_url = BASE_URL, user_agent = USER_AGENT, client = Excon, sleep_time: 3)
|
16
|
+
@base_url = base_url
|
17
|
+
@user_agent = user_agent
|
18
|
+
@token = token
|
19
|
+
@client = client
|
20
|
+
@out = out
|
21
|
+
@sleep_time = sleep_time
|
22
|
+
end
|
23
|
+
|
24
|
+
def org(login, debug = false)
|
25
|
+
result = get("/orgs/#{login}", debug: debug)
|
26
|
+
|
27
|
+
{
|
28
|
+
id: result.fetch("id"),
|
29
|
+
login: result.fetch("login")
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def org_members(org, debug = false)
|
34
|
+
result = get_paginated("/orgs/#{org.fetch(:login)}/members", debug: debug)
|
35
|
+
result.map do |user|
|
36
|
+
{
|
37
|
+
id: user.fetch("id"),
|
38
|
+
login: user.fetch("login")
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def teams(org, debug = false)
|
44
|
+
result = get_paginated("/orgs/#{org.fetch(:login)}/teams", debug: debug)
|
45
|
+
result.map do |team|
|
46
|
+
{
|
47
|
+
id: team.fetch("id"),
|
48
|
+
org_id: org.fetch(:id),
|
49
|
+
name: team.fetch("name"),
|
50
|
+
slug: team.fetch("slug")
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def team_members(org, teams, debug = false)
|
56
|
+
teams.each_with_object([]) do |team, memo|
|
57
|
+
result = get_paginated("/orgs/#{org.fetch(:login)}/teams/#{team.fetch(:slug)}/members", debug: debug)
|
58
|
+
result.each do |member|
|
59
|
+
team_id = team.fetch(:id)
|
60
|
+
user_id = member.fetch("id")
|
61
|
+
|
62
|
+
memo << {
|
63
|
+
id: [team_id, user_id],
|
64
|
+
team_id: team_id,
|
65
|
+
user_id: user_id
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
sleep_for_a_while
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def users(users, debug)
|
74
|
+
users.each do |user|
|
75
|
+
remote_user = get("/users/#{user.fetch(:login)}", debug: debug)
|
76
|
+
user.merge!(
|
77
|
+
name: remote_user.fetch("name"),
|
78
|
+
email: remote_user.fetch("email")
|
79
|
+
)
|
80
|
+
|
81
|
+
sleep_for_a_while
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
attr_reader :base_url
|
88
|
+
attr_reader :user_agent
|
89
|
+
attr_reader :token
|
90
|
+
attr_reader :client
|
91
|
+
attr_reader :out
|
92
|
+
attr_reader :sleep_time
|
93
|
+
|
94
|
+
def get(path, debug: false)
|
95
|
+
out.puts "requesting GET #{path}" if debug
|
96
|
+
|
97
|
+
response = client.get(base_url + path, query: query, headers: headers)
|
98
|
+
return {} unless response.status == 200
|
99
|
+
|
100
|
+
JSON.parse(response.body)
|
101
|
+
end
|
102
|
+
|
103
|
+
def get_paginated(path, result = [], debug: false, page: 1)
|
104
|
+
out.puts "requesting GET #{path}, page: #{page}" if debug
|
105
|
+
|
106
|
+
response = client.get(base_url + path, query: query(page: page), headers: headers)
|
107
|
+
return [] unless response.status == 200
|
108
|
+
|
109
|
+
parsed = JSON.parse(response.body)
|
110
|
+
result.push(parsed)
|
111
|
+
|
112
|
+
if parsed.any?
|
113
|
+
sleep_for_a_while
|
114
|
+
get_paginated(path, result, debug: debug, page: page + 1)
|
115
|
+
else
|
116
|
+
result.flatten
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def query(options = {})
|
121
|
+
{ page: 1, per_page: 100 }.merge(options)
|
122
|
+
end
|
123
|
+
|
124
|
+
def headers
|
125
|
+
{
|
126
|
+
"Authorization" => "token #{token}",
|
127
|
+
"User-Agent" => user_agent
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
131
|
+
def sleep_for_a_while
|
132
|
+
sleep(sleep_time)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Codeowners
|
4
|
+
module Import
|
5
|
+
class Organization
|
6
|
+
def initialize(client, storage)
|
7
|
+
@client = client
|
8
|
+
@storage = storage
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(org, debug)
|
12
|
+
org = client.org(org, debug)
|
13
|
+
users = client.org_members(org, debug)
|
14
|
+
users = client.users(users, debug)
|
15
|
+
teams = client.teams(org, debug)
|
16
|
+
memberships = client.team_members(org, teams, debug)
|
17
|
+
|
18
|
+
storage.transaction do |db|
|
19
|
+
db[:orgs].upsert(org)
|
20
|
+
db[:users].upsert(users)
|
21
|
+
db[:teams].upsert(teams)
|
22
|
+
db[:memberships].upsert(memberships)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :client
|
29
|
+
attr_reader :storage
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/codeowners/result.rb
CHANGED
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "tempfile"
|
5
|
+
require "fileutils"
|
6
|
+
|
7
|
+
module Codeowners
|
8
|
+
class Storage
|
9
|
+
require "codeowners/storage/data"
|
10
|
+
|
11
|
+
def initialize(path)
|
12
|
+
@path = path
|
13
|
+
@data = Data.new(load_data)
|
14
|
+
@mutex = Mutex.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def transaction
|
18
|
+
@mutex.synchronize do
|
19
|
+
tmp_data = data.dup
|
20
|
+
yield tmp_data
|
21
|
+
|
22
|
+
Tempfile.open("codeowners-storage") do |tmp|
|
23
|
+
tmp.binmode
|
24
|
+
tmp.write(JSON.generate(data.dump))
|
25
|
+
tmp.close
|
26
|
+
|
27
|
+
FileUtils.mv(tmp.path, path, force: true)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def team_exist?(team)
|
33
|
+
data[:teams].find do |record|
|
34
|
+
record.fetch("slug") == team
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def blacklisted_team?(team)
|
39
|
+
data[:teams].find do |record|
|
40
|
+
record.fetch("slug") == team &&
|
41
|
+
record.fetch("blacklisted")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def teams_for(user)
|
46
|
+
found = data[:users].find do |record|
|
47
|
+
record.fetch("email") == user.email ||
|
48
|
+
record.fetch("name") == user.name
|
49
|
+
end
|
50
|
+
|
51
|
+
return [] unless found
|
52
|
+
|
53
|
+
memberships = data[:memberships].find_all do |record|
|
54
|
+
record.fetch("user_id") == found.fetch("id")
|
55
|
+
end
|
56
|
+
memberships.map! { |hash| hash.fetch("team_id") }
|
57
|
+
|
58
|
+
return [] if memberships.empty?
|
59
|
+
|
60
|
+
teams = data[:teams].find_all do |record|
|
61
|
+
!record.fetch("blacklisted") &&
|
62
|
+
memberships.include?(record.fetch("id"))
|
63
|
+
end.flatten
|
64
|
+
|
65
|
+
teams
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
attr_reader :path
|
71
|
+
attr_reader :data
|
72
|
+
|
73
|
+
def load_data
|
74
|
+
return {} unless File.exist?(path)
|
75
|
+
|
76
|
+
JSON.parse(File.read(path))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Codeowners
|
4
|
+
class Storage
|
5
|
+
class Collection
|
6
|
+
def initialize(collection)
|
7
|
+
@collection = collection.each_with_object({}) do |record, memo|
|
8
|
+
memo[record.fetch("id")] = record
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def find(&blk)
|
13
|
+
collection.values.find(&blk)
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_all(&blk)
|
17
|
+
collection.values.find_all(&blk)
|
18
|
+
end
|
19
|
+
|
20
|
+
def upsert(*records)
|
21
|
+
records = Array(records).flatten
|
22
|
+
|
23
|
+
records.each do |record|
|
24
|
+
collection[record.fetch(:id)] = record
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def dump
|
29
|
+
collection.values.dup
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :collection
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "codeowners/storage/collection"
|
4
|
+
|
5
|
+
module Codeowners
|
6
|
+
class Storage
|
7
|
+
class Data
|
8
|
+
COLLECTIONS = %w[orgs users teams memberships].freeze
|
9
|
+
private_constant :COLLECTIONS
|
10
|
+
|
11
|
+
def initialize(data, collections: COLLECTIONS)
|
12
|
+
@data = collections.each_with_object({}) do |name, memo|
|
13
|
+
memo[name] = Collection.new(data.fetch(name, []))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](name)
|
18
|
+
data.fetch(name.to_s)
|
19
|
+
end
|
20
|
+
|
21
|
+
def dump
|
22
|
+
data.transform_values(&:dump)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :data
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/codeowners/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: codeowners
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luca Guidi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-07-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-cli
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: excon
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.75'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.75'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rubocop
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -76,9 +90,15 @@ files:
|
|
76
90
|
- lib/codeowners/git.rb
|
77
91
|
- lib/codeowners/git/contributor.rb
|
78
92
|
- lib/codeowners/git/contributors.rb
|
93
|
+
- lib/codeowners/guess.rb
|
94
|
+
- lib/codeowners/import/client.rb
|
95
|
+
- lib/codeowners/import/organization.rb
|
79
96
|
- lib/codeowners/list_contributors.rb
|
80
97
|
- lib/codeowners/list_owners.rb
|
81
98
|
- lib/codeowners/result.rb
|
99
|
+
- lib/codeowners/storage.rb
|
100
|
+
- lib/codeowners/storage/collection.rb
|
101
|
+
- lib/codeowners/storage/data.rb
|
82
102
|
- lib/codeowners/version.rb
|
83
103
|
homepage: https://lucaguidi.com
|
84
104
|
licenses: []
|