code_ownership 1.38.2 → 1.39.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/README.md +2 -0
- data/lib/code_ownership/configuration.rb +3 -1
- data/lib/code_ownership/private/codeowners_file.rb +4 -1
- data/lib/code_ownership/private/owner_assigner.rb +1 -1
- data/lib/code_ownership/private/ownership_mappers/file_annotations.rb +9 -8
- data/lib/code_ownership/private/ownership_mappers/team_globs.rb +19 -13
- data/lib/code_ownership/private/team_plugins/ownership.rb +5 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4750c985a93873263fe3f86ee3acfa80b3bb9a234445b2cacfdb57dd657916e4
|
4
|
+
data.tar.gz: 4e672aeeeaa93addd346d0a01c0dc135dd576e4e196a91124e8f7e31f7045da1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39b311ddd4a120865344fea860189c452d7adb8b88d56ce3cd6add99a376bb8337cb27d4d741c1fc44fa473b4a15866a50c9e18d08f47f9ebb8bfb57106a9c7f
|
7
|
+
data.tar.gz: 1355b25ee4a54d09dd6722aca4f11ad410521414cca07fdf7ccafba299ccc7482cbfa97b58c0c198488eacdf12c479aa1777c1acfcee7305f5454d60441c44ad
|
data/README.md
CHANGED
@@ -164,6 +164,8 @@ bin/codeownership for_team 'My Team' > tmp/ownership_report.md
|
|
164
164
|
|
165
165
|
A `CODEOWNERS` file defines who owns specific files or paths in a repository. When you run `bin/codeownership validate`, a `.github/CODEOWNERS` file will automatically be generated and updated.
|
166
166
|
|
167
|
+
If `codeowners_path` is set in `code_ownership.yml` codeowners will use that path to generate the `CODEOWNERS` file. For example, `codeowners_path: docs` will generate `docs/CODEOWNERS`.
|
168
|
+
|
167
169
|
## Proper Configuration & Validation
|
168
170
|
|
169
171
|
CodeOwnership comes with a validation function to ensure the following things are true:
|
@@ -12,6 +12,7 @@ module CodeOwnership
|
|
12
12
|
const :skip_codeowners_validation, T::Boolean
|
13
13
|
const :raw_hash, T::Hash[T.untyped, T.untyped]
|
14
14
|
const :require_github_teams, T::Boolean
|
15
|
+
const :codeowners_path, String
|
15
16
|
|
16
17
|
sig { returns(Configuration) }
|
17
18
|
def self.fetch
|
@@ -29,7 +30,8 @@ module CodeOwnership
|
|
29
30
|
js_package_paths: js_package_paths(config_hash),
|
30
31
|
skip_codeowners_validation: config_hash.fetch('skip_codeowners_validation', false),
|
31
32
|
raw_hash: config_hash,
|
32
|
-
require_github_teams: config_hash.fetch('require_github_teams', false)
|
33
|
+
require_github_teams: config_hash.fetch('require_github_teams', false),
|
34
|
+
codeowners_path: config_hash.fetch('codeowners_path', '.github'),
|
33
35
|
)
|
34
36
|
end
|
35
37
|
|
@@ -111,7 +111,10 @@ module CodeOwnership
|
|
111
111
|
|
112
112
|
sig { returns(Pathname) }
|
113
113
|
def self.path
|
114
|
-
Pathname.pwd.join(
|
114
|
+
Pathname.pwd.join(
|
115
|
+
CodeOwnership.configuration.codeowners_path,
|
116
|
+
'CODEOWNERS'
|
117
|
+
)
|
115
118
|
end
|
116
119
|
|
117
120
|
sig { params(files: T::Array[String]).void }
|
@@ -12,7 +12,7 @@ module CodeOwnership
|
|
12
12
|
# addresses the case where a directory name includes regex characters
|
13
13
|
# such as `app/services/[test]/some_other_file.ts`
|
14
14
|
mapping[glob] = owner if File.exist?(glob)
|
15
|
-
Dir.glob(glob)
|
15
|
+
Dir.glob(glob) do |file|
|
16
16
|
mapping[file] ||= owner
|
17
17
|
end
|
18
18
|
end
|
@@ -18,7 +18,7 @@ module CodeOwnership
|
|
18
18
|
extend T::Sig
|
19
19
|
include Mapper
|
20
20
|
|
21
|
-
TEAM_PATTERN = T.let(%r{\A(
|
21
|
+
TEAM_PATTERN = T.let(%r{\A(?:#|//|-#) @team (?<team>.*)\Z}.freeze, Regexp)
|
22
22
|
DESCRIPTION = 'Annotations at the top of file'
|
23
23
|
|
24
24
|
sig do
|
@@ -73,19 +73,20 @@ module CodeOwnership
|
|
73
73
|
|
74
74
|
sig { params(filename: String).returns(T.nilable(CodeTeams::Team)) }
|
75
75
|
def file_annotation_based_owner(filename)
|
76
|
-
# If for a directory is named with an ownable extension, we need to skip
|
77
|
-
# so File.foreach doesn't blow up below. This was needed because Cypress
|
78
|
-
# screenshots are saved to a folder with the test suite filename.
|
79
|
-
return if File.directory?(filename)
|
80
|
-
return unless File.file?(filename)
|
81
|
-
|
82
76
|
# The annotation should be on line 1 but as of this comment
|
83
77
|
# there's no linter installed to enforce that. We therefore check the
|
84
78
|
# first line (the Ruby VM makes a single `read(1)` call for 8KB),
|
85
79
|
# and if the annotation isn't in the first two lines we assume it
|
86
80
|
# doesn't exist.
|
87
81
|
|
88
|
-
|
82
|
+
begin
|
83
|
+
line1 = File.foreach(filename).first
|
84
|
+
rescue Errno::EISDIR, Errno::ENOENT
|
85
|
+
# Ignore files that fail to read to avoid intermittent bugs.
|
86
|
+
# Ignoring directories is needed because, e.g., Cypress screenshots
|
87
|
+
# are saved to a folder with the test suite filename.
|
88
|
+
return
|
89
|
+
end
|
89
90
|
|
90
91
|
return if !line1
|
91
92
|
|
@@ -14,17 +14,16 @@ module CodeOwnership
|
|
14
14
|
@@map_files_to_owners = {} # rubocop:disable Style/ClassVars
|
15
15
|
|
16
16
|
sig do
|
17
|
-
|
18
|
-
.returns(T::Hash[String, ::CodeTeams::Team])
|
17
|
+
returns(T::Hash[String, ::CodeTeams::Team])
|
19
18
|
end
|
20
|
-
def map_files_to_owners
|
19
|
+
def map_files_to_owners
|
21
20
|
return @@map_files_to_owners if @@map_files_to_owners&.keys && @@map_files_to_owners.keys.count.positive?
|
22
21
|
|
23
22
|
@@map_files_to_owners = CodeTeams.all.each_with_object({}) do |team, map| # rubocop:disable Style/ClassVars
|
24
|
-
TeamPlugins::Ownership.for(team)
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
code_team = TeamPlugins::Ownership.for(team)
|
24
|
+
|
25
|
+
(Dir.glob(code_team.owned_globs) - Dir.glob(code_team.unowned_globs)).each do |filename|
|
26
|
+
map[filename] = team
|
28
27
|
end
|
29
28
|
end
|
30
29
|
end
|
@@ -56,13 +55,20 @@ module CodeOwnership
|
|
56
55
|
end
|
57
56
|
def find_overlapping_globs
|
58
57
|
mapped_files = T.let({}, T::Hash[String, T::Array[MappingContext]])
|
59
|
-
CodeTeams.all.
|
60
|
-
TeamPlugins::Ownership.for(team)
|
61
|
-
|
58
|
+
CodeTeams.all.each do |team|
|
59
|
+
code_team = TeamPlugins::Ownership.for(team)
|
60
|
+
|
61
|
+
code_team.owned_globs.each do |glob|
|
62
|
+
Dir.glob(glob) do |filename|
|
62
63
|
mapped_files[filename] ||= []
|
63
64
|
T.must(mapped_files[filename]) << MappingContext.new(glob: glob, team: team)
|
64
65
|
end
|
65
66
|
end
|
67
|
+
|
68
|
+
# Remove anything that is unowned, globbing them all at once
|
69
|
+
Dir.glob(code_team.unowned_globs) do |filename|
|
70
|
+
mapped_files.reject! { |key, value| key == filename && value.any? { |context| context.team == team } }
|
71
|
+
end
|
66
72
|
end
|
67
73
|
|
68
74
|
overlaps = T.let([], T::Array[GlobOverlap])
|
@@ -81,10 +87,10 @@ module CodeOwnership
|
|
81
87
|
|
82
88
|
sig do
|
83
89
|
override.params(file: String)
|
84
|
-
|
90
|
+
.returns(T.nilable(::CodeTeams::Team))
|
85
91
|
end
|
86
92
|
def map_file_to_owner(file)
|
87
|
-
map_files_to_owners
|
93
|
+
map_files_to_owners[file]
|
88
94
|
end
|
89
95
|
|
90
96
|
sig do
|
@@ -96,7 +102,7 @@ module CodeOwnership
|
|
96
102
|
|
97
103
|
sig do
|
98
104
|
override.params(files: T::Array[String])
|
99
|
-
|
105
|
+
.returns(T::Hash[String, ::CodeTeams::Team])
|
100
106
|
end
|
101
107
|
def globs_to_owner(files)
|
102
108
|
CodeTeams.all.each_with_object({}) do |team, map|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: code_ownership
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.39.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gusto Engineers
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-01
|
10
|
+
date: 2025-03-01 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: code_teams
|