Skip to content

Commit 72268ed

Browse files
committed
updating code_ownership docs
1 parent 014d4a7 commit 72268ed

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed

lib/code_ownership.rb

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,37 @@ module CodeOwnership
3434
requires_ancestor { Kernel }
3535
GlobsToOwningTeamMap = T.type_alias { T::Hash[String, CodeTeams::Team] }
3636

37+
# Returns the version of the code_ownership gem and the codeowners-rs gem.
3738
sig { returns(T::Array[String]) }
3839
def version
3940
["code_ownership version: #{VERSION}",
4041
"codeowners-rs version: #{::RustCodeOwners.version}"]
4142
end
4243

44+
# Returns the owning team for a given file path.
45+
#
46+
# @param file [String] The path to the file to find ownership for. Can be relative or absolute.
47+
# @param from_codeowners [Boolean] (default: true) When true, uses CODEOWNERS file to determine ownership.
48+
# When false, uses alternative team finding strategies (e.g., package ownership).
49+
# from_codeowners true is faster because it simply matches the provided file to the generate CODEOWNERS file. This is a safe option when you can trust the CODEOWNERS file to be up to date.
50+
# @param allow_raise [Boolean] (default: false) When true, raises an exception if ownership cannot be determined.
51+
# When false, returns nil for files without ownership.
52+
#
53+
# @return [CodeTeams::Team, nil] The team that owns the file, or nil if no owner is found
54+
# (unless allow_raise is true, in which case an exception is raised).
55+
#
56+
# @example Find owner for a file using CODEOWNERS
57+
# team = CodeOwnership.for_file('app/models/user.rb')
58+
# # => #<CodeTeams::Team:0x... @name="platform">
59+
#
60+
# @example Find owner without using CODEOWNERS
61+
# team = CodeOwnership.for_file('app/models/user.rb', from_codeowners: false)
62+
# # => #<CodeTeams::Team:0x... @name="platform">
63+
#
64+
# @example Raise if no owner is found
65+
# team = CodeOwnership.for_file('unknown_file.rb', allow_raise: true)
66+
# # => raises exception if no owner found
67+
#
4368
sig { params(file: String, from_codeowners: T::Boolean, allow_raise: T::Boolean).returns(T.nilable(CodeTeams::Team)) }
4469
def for_file(file, from_codeowners: true, allow_raise: false)
4570
if from_codeowners
@@ -49,11 +74,92 @@ def for_file(file, from_codeowners: true, allow_raise: false)
4974
end
5075
end
5176

77+
# Returns the owning teams for multiple file paths using the CODEOWNERS file.
78+
#
79+
# This method efficiently determines ownership for multiple files in a single operation
80+
# by leveraging the generated CODEOWNERS file. It's more performant than calling
81+
# `for_file` multiple times when you need to check ownership for many files.
82+
#
83+
# @param files [Array<String>] An array of file paths to find ownership for.
84+
# Paths can be relative to the project root or absolute.
85+
# @param allow_raise [Boolean] (default: false) When true, raises an exception if a team
86+
# name in CODEOWNERS cannot be resolved to an actual team.
87+
# When false, returns nil for files with unresolvable teams.
88+
#
89+
# @return [T::Hash[String, T.nilable(CodeTeams::Team)]] A hash mapping each file path to its
90+
# owning team. Files without ownership
91+
# or with unresolvable teams will map to nil.
92+
#
93+
# @example Get owners for multiple files
94+
# files = ['app/models/user.rb', 'app/controllers/users_controller.rb', 'config/routes.rb']
95+
# owners = CodeOwnership.teams_for_files_from_codeowners(files)
96+
# # => {
97+
# # 'app/models/user.rb' => #<CodeTeams::Team:0x... @name="platform">,
98+
# # 'app/controllers/users_controller.rb' => #<CodeTeams::Team:0x... @name="platform">,
99+
# # 'config/routes.rb' => #<CodeTeams::Team:0x... @name="infrastructure">
100+
# # }
101+
#
102+
# @example Handle files without owners
103+
# files = ['owned_file.rb', 'unowned_file.txt']
104+
# owners = CodeOwnership.teams_for_files_from_codeowners(files)
105+
# # => {
106+
# # 'owned_file.rb' => #<CodeTeams::Team:0x... @name="backend">,
107+
# # 'unowned_file.txt' => nil
108+
# # }
109+
#
110+
# @note This method uses caching internally for performance. The cache is populated
111+
# as files are processed and reused for subsequent lookups.
112+
#
113+
# @note This method relies on the CODEOWNERS file being up-to-date. Run
114+
# `CodeOwnership.validate!` to ensure the CODEOWNERS file is current.
115+
#
116+
# @see #for_file for single file ownership lookup
117+
# @see #validate! for ensuring CODEOWNERS file is up-to-date
118+
#
52119
sig { params(files: T::Array[String], allow_raise: T::Boolean).returns(T::Hash[String, T.nilable(CodeTeams::Team)]) }
53120
def teams_for_files_from_codeowners(files, allow_raise: false)
54121
Private::TeamFinder.teams_for_files(files, allow_raise: allow_raise)
55122
end
56123

124+
# Returns detailed ownership information for a given file path.
125+
#
126+
# This method provides verbose ownership details including the team name,
127+
# team configuration file path, and the reasons/sources for ownership assignment.
128+
# It's particularly useful for debugging ownership assignments and understanding
129+
# why a file is owned by a specific team.
130+
#
131+
# @param file [String] The path to the file to find ownership for. Can be relative or absolute.
132+
#
133+
# @return [T::Hash[Symbol, String], nil] A hash containing detailed ownership information,
134+
# or nil if no owner is found.
135+
#
136+
# The returned hash contains the following keys when an owner is found:
137+
# - :team_name [String] - The name of the owning team
138+
# - :team_config_yml [String] - Path to the team's configuration YAML file
139+
# - :reasons [Array<String>] - List of reasons/sources explaining why this team owns the file
140+
# (e.g., "CODEOWNERS pattern: /app/models/**", "Package ownership")
141+
#
142+
# @example Get verbose ownership details
143+
# details = CodeOwnership.for_file_verbose('app/models/user.rb')
144+
# # => {
145+
# # team_name: "platform",
146+
# # team_config_yml: "config/teams/platform.yml",
147+
# # reasons: ["Matched pattern '/app/models/**' in CODEOWNERS"]
148+
# # }
149+
#
150+
# @example Handle unowned files
151+
# details = CodeOwnership.for_file_verbose('unowned_file.txt')
152+
# # => nil
153+
#
154+
# @note This method is primarily used by the CLI tool when the --verbose flag is provided,
155+
# allowing users to understand the ownership assignment logic.
156+
#
157+
# @note Unlike `for_file`, this method always uses the CODEOWNERS file and other ownership
158+
# sources to determine ownership, providing complete context about the ownership decision.
159+
#
160+
# @see #for_file for a simpler ownership lookup that returns just the team
161+
# @see CLI#for_file for the command-line interface that uses this method
162+
#
57163
sig { params(file: String).returns(T.nilable(T::Hash[Symbol, String])) }
58164
def for_file_verbose(file)
59165
::RustCodeOwners.for_file(file)
@@ -65,6 +171,55 @@ def for_team(team)
65171
::RustCodeOwners.for_team(team.name)
66172
end
67173

174+
# Validates code ownership configuration and optionally corrects issues.
175+
#
176+
# This method performs comprehensive validation of the code ownership setup, ensuring:
177+
# 1. Only one ownership mechanism is defined per file (no conflicts between annotations, packages, or globs)
178+
# 2. All referenced teams are valid (exist in CodeTeams configuration)
179+
# 3. All files have ownership (unless explicitly listed in unowned_globs)
180+
# 4. The .github/CODEOWNERS file is up-to-date and properly formatted
181+
#
182+
# When autocorrect is enabled, the method will automatically:
183+
# - Generate or update the CODEOWNERS file based on current ownership rules
184+
# - Fix any formatting issues in the CODEOWNERS file
185+
# - Stage the corrected CODEOWNERS file (unless stage_changes is false)
186+
#
187+
# @param autocorrect [Boolean] Whether to automatically fix correctable issues (default: true)
188+
# When true, regenerates and updates the CODEOWNERS file
189+
# When false, only validates without making changes
190+
#
191+
# @param stage_changes [Boolean] Whether to stage the CODEOWNERS file after autocorrection (default: true)
192+
# Only applies when autocorrect is true
193+
# When false, changes are written but not staged with git
194+
#
195+
# @param files [Array<String>, nil] Ignored. This is a legacy parameter that is no longer used.
196+
#
197+
# @return [void]
198+
#
199+
# @raise [RuntimeError] Raises an error if validation fails with details about:
200+
# - Files with conflicting ownership definitions
201+
# - References to non-existent teams
202+
# - Files without ownership (not in unowned_globs)
203+
# - CODEOWNERS file inconsistencies
204+
#
205+
# @example Basic validation with autocorrection
206+
# CodeOwnership.validate!
207+
# # Validates all files and auto-corrects/stages CODEOWNERS if needed
208+
#
209+
# @example Validation without making changes
210+
# CodeOwnership.validate!(autocorrect: false)
211+
# # Only checks for issues without updating CODEOWNERS
212+
#
213+
# @example Validate and fix but don't stage changes
214+
# CodeOwnership.validate!(autocorrect: true, stage_changes: false)
215+
# # Fixes CODEOWNERS but doesn't stage it with git
216+
#
217+
# @note This method is called by the CLI command: bin/codeownership validate
218+
# @note The validation can be disabled for CODEOWNERS by setting skip_codeowners_validation: true in config/code_ownership.yml
219+
#
220+
# @see CLI.validate! for the command-line interface
221+
# @see https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners for CODEOWNERS format
222+
#
68223
sig do
69224
params(
70225
autocorrect: T::Boolean,
0 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)