diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..64b735d --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,123 @@ +# The behavior of RuboCop can be controlled via the .rubocop.yml +# configuration file. It makes it possible to enable/disable +# certain cops (checks) and to alter their behavior if they accept +# any parameters. The file can be placed either in your home +# directory or in some project directory. +# +# RuboCop will start looking for the configuration file in the directory +# where the inspected file is and continue its way up to the root directory. +# +# See https://docs.rubocop.org/rubocop/configuration +AllCops: + NewCops: enable + Exclude: + - vendor/bundle/**/** + TargetRubyVersion: 2.6 + +Metrics/ParameterLists: + Enabled: false + +# This cop is annoying with typed configuration +Style/TrivialAccessors: + Enabled: false + +# This rubocop is annoying when we use interfaces a lot +Lint/UnusedMethodArgument: + Enabled: false + +Gemspec/RequireMFA: + Enabled: false + +Lint/DuplicateBranch: + Enabled: false + +# If is sometimes easier to think about than unless sometimes +Style/NegatedIf: + Enabled: false + +# Disabling for now until it's clearer why we want this +Style/FrozenStringLiteralComment: + Enabled: false + +# It's nice to be able to read the condition first before reading the code within the condition +Style/GuardClause: + Enabled: false + +# +# Leaving length metrics to human judgment for now +# +Metrics/ModuleLength: + Enabled: false + +Layout/LineLength: + Enabled: false + +Metrics/BlockLength: + Enabled: false + +Metrics/MethodLength: + Enabled: false + +Metrics/AbcSize: + Enabled: false + +Metrics/ClassLength: + Enabled: false + +# This doesn't feel useful +Metrics/CyclomaticComplexity: + Enabled: false + +# This doesn't feel useful +Metrics/PerceivedComplexity: + Enabled: false + +# It's nice to be able to read the condition first before reading the code within the condition +Style/IfUnlessModifier: + Enabled: false + +# This leads to code that is not very readable at times (very long lines) +Style/ConditionalAssignment: + Enabled: false + +# For now, we prefer to lean on clean method signatures as documentation. We may change this later. +Style/Documentation: + Enabled: false + +# Sometimes we leave comments in empty else statements intentionally +Style/EmptyElse: + Enabled: false + +# Sometimes we want to more explicitly list out a condition +Style/RedundantCondition: + Enabled: false + +# This leads to code that is not very readable at times (very long lines) +Layout/MultilineMethodCallIndentation: + Enabled: false + +# Blocks across lines are okay sometimes +Style/BlockDelimiters: + Enabled: false + +# Sometimes we like methods like `get_packages` +Naming/AccessorMethodName: + Enabled: false + +# This leads to code that is not very readable at times (very long lines) +Layout/FirstArgumentIndentation: + Enabled: false + +# This leads to code that is not very readable at times (very long lines) +Layout/ArgumentAlignment: + Enabled: false + +Style/AccessorGrouping: + Enabled: false + +Style/HashSyntax: + Enabled: false + +Gemspec/DevelopmentDependencies: + Enabled: true + EnforcedStyle: gemspec \ No newline at end of file diff --git a/code_ownership.gemspec b/code_ownership.gemspec index f2b3327..c78af40 100644 --- a/code_ownership.gemspec +++ b/code_ownership.gemspec @@ -7,6 +7,7 @@ Gem::Specification.new do |spec| spec.description = 'A gem to help engineering teams declare ownership of code' spec.homepage = 'https://github.com/rubyatscale/code_ownership' spec.license = 'MIT' + spec.required_ruby_version = '>= 2.6' if spec.respond_to?(:metadata) spec.metadata['homepage_uri'] = spec.homepage @@ -31,10 +32,11 @@ Gem::Specification.new do |spec| spec.add_dependency 'sorbet-runtime', '>= 0.5.11249' spec.add_development_dependency 'debug' + spec.add_development_dependency 'packwerk' + spec.add_development_dependency 'railties' spec.add_development_dependency 'rake' spec.add_development_dependency 'rspec', '~> 3.0' + spec.add_development_dependency 'rubocop' spec.add_development_dependency 'sorbet' spec.add_development_dependency 'tapioca' - spec.add_development_dependency 'packwerk' - spec.add_development_dependency 'railties' end diff --git a/lib/code_ownership.rb b/lib/code_ownership.rb index 9ffb54a..f3656a7 100644 --- a/lib/code_ownership.rb +++ b/lib/code_ownership.rb @@ -18,7 +18,8 @@ end module CodeOwnership - extend self + module_function + extend T::Sig extend T::Helpers @@ -57,6 +58,7 @@ def for_team(team) ownership_for_mapper = [] glob_to_owning_team_map.each do |glob, owning_team| next if owning_team != team + ownership_for_mapper << "- #{glob}" end @@ -66,7 +68,7 @@ def for_team(team) ownership_information += ownership_for_mapper.sort end - ownership_information << "" + ownership_information << '' end ownership_information.join("\n") @@ -84,7 +86,7 @@ def self.remove_file_annotation!(filename) params( autocorrect: T::Boolean, stage_changes: T::Boolean, - files: T.nilable(T::Array[String]), + files: T.nilable(T::Array[String]) ).void end def validate!( @@ -95,10 +97,10 @@ def validate!( Private.load_configuration! tracked_file_subset = if files - files.select{|f| Private.file_tracked?(f)} - else - Private.tracked_files - end + files.select { |f| Private.file_tracked?(f) } + else + Private.tracked_files + end Private.validate!(files: tracked_file_subset, autocorrect: autocorrect, stage_changes: stage_changes) end @@ -150,7 +152,7 @@ def backtrace_with_ownership(backtrace) [ CodeOwnership.for_file(file), - file, + file ] end end @@ -161,15 +163,15 @@ def for_class(klass) @memoized_values ||= T.let(@memoized_values, T.nilable(T::Hash[String, T.nilable(::CodeTeams::Team)])) @memoized_values ||= {} # We use key because the memoized value could be `nil` - if !@memoized_values.key?(klass.to_s) + if @memoized_values.key?(klass.to_s) + @memoized_values[klass.to_s] + else path = Private.path_from_klass(klass) return nil if path.nil? value_to_memoize = for_file(path) @memoized_values[klass.to_s] = value_to_memoize value_to_memoize - else - @memoized_values[klass.to_s] end end diff --git a/lib/code_ownership/cli.rb b/lib/code_ownership/cli.rb index c391b96..e4c8a25 100644 --- a/lib/code_ownership/cli.rb +++ b/lib/code_ownership/cli.rb @@ -14,7 +14,7 @@ def self.run!(argv) for_file(argv) elsif command == 'for_team' for_team(argv) - elsif [nil, "help"].include?(command) + elsif [nil, 'help'].include?(command) puts <<~USAGE Usage: bin/codeownership @@ -27,7 +27,6 @@ def self.run!(argv) else puts "'#{command}' is not a code_ownership command. See `bin/codeownership help`." end - end def self.validate!(argv) @@ -53,16 +52,16 @@ def self.validate!(argv) exit end end - args = parser.order!(argv) {} + args = parser.order!(argv) parser.parse!(args) files = if options[:diff] - ENV.fetch('CODEOWNERS_GIT_STAGED_FILES') { `git diff --staged --name-only` }.split("\n").select do |file| - File.exist?(file) - end - else - nil - end + ENV.fetch('CODEOWNERS_GIT_STAGED_FILES') { `git diff --staged --name-only` }.split("\n").select do |file| + File.exist?(file) + end + else + nil + end CodeOwnership.validate!( files: files, @@ -79,7 +78,7 @@ def self.for_file(argv) # Long-term, we probably want to use something like `thor` so we don't have to implement logic # like this. In the short-term, this is a simple way for us to use the built-in OptionParser # while having an ergonomic CLI. - files = argv.select { |arg| !arg.start_with?('--') } + files = argv.reject { |arg| arg.start_with?('--') } parser = OptionParser.new do |opts| opts.banner = 'Usage: bin/codeownership for_file [options]' @@ -93,22 +92,22 @@ def self.for_file(argv) exit end end - args = parser.order!(argv) {} + args = parser.order!(argv) parser.parse!(args) if files.count != 1 - raise "Please pass in one file. Use `bin/codeownership for_file --help` for more info" + raise 'Please pass in one file. Use `bin/codeownership for_file --help` for more info' end - + team = CodeOwnership.for_file(files.first) - team_name = team&.name || "Unowned" - team_yml = team&.config_yml || "Unowned" + team_name = team&.name || 'Unowned' + team_yml = team&.config_yml || 'Unowned' if options[:json] json = { team_name: team_name, - team_yml: team_yml, + team_yml: team_yml } puts json.to_json @@ -121,8 +120,6 @@ def self.for_file(argv) end def self.for_team(argv) - options = {} - parser = OptionParser.new do |opts| opts.banner = 'Usage: bin/codeownership for_team \'Team Name\'' @@ -131,14 +128,14 @@ def self.for_team(argv) exit end end - teams = argv.select { |arg| !arg.start_with?('--') } - args = parser.order!(argv) {} + teams = argv.reject { |arg| arg.start_with?('--') } + args = parser.order!(argv) parser.parse!(args) if teams.count != 1 - raise "Please pass in one team. Use `bin/codeownership for_team --help` for more info" + raise 'Please pass in one team. Use `bin/codeownership for_team --help` for more info' end - + puts CodeOwnership.for_team(teams.first) end diff --git a/lib/code_ownership/configuration.rb b/lib/code_ownership/configuration.rb index 295091c..33bef67 100644 --- a/lib/code_ownership/configuration.rb +++ b/lib/code_ownership/configuration.rb @@ -17,8 +17,8 @@ class Configuration < T::Struct def self.fetch config_hash = YAML.load_file('config/code_ownership.yml') - if config_hash.key?("require") - config_hash["require"].each do |require_directive| + if config_hash.key?('require') + config_hash['require'].each do |require_directive| Private::ExtensionLoader.load(require_directive) end end diff --git a/lib/code_ownership/mapper.rb b/lib/code_ownership/mapper.rb index 4aaa65f..31835bb 100644 --- a/lib/code_ownership/mapper.rb +++ b/lib/code_ownership/mapper.rb @@ -29,21 +29,19 @@ def all # This should be fast when run with ONE file # sig do - abstract.params(file: String). - returns(T.nilable(::CodeTeams::Team)) - end - def map_file_to_owner(file) + abstract.params(file: String) + .returns(T.nilable(::CodeTeams::Team)) end + def map_file_to_owner(file); end # # This should be fast when run with MANY files # sig do - abstract.params(files: T::Array[String]). - returns(T::Hash[String, ::CodeTeams::Team]) - end - def globs_to_owner(files) + abstract.params(files: T::Array[String]) + .returns(T::Hash[String, ::CodeTeams::Team]) end + def globs_to_owner(files); end # # This should be fast when run with MANY files @@ -51,16 +49,13 @@ def globs_to_owner(files) sig do abstract.params(cache: GlobsToOwningTeamMap, files: T::Array[String]).returns(GlobsToOwningTeamMap) end - def update_cache(cache, files) - end + def update_cache(cache, files); end sig { abstract.returns(String) } - def description - end + def description; end sig { abstract.void } - def bust_caches! - end + def bust_caches!; end sig { returns(Private::GlobCache) } def self.to_glob_cache @@ -72,6 +67,7 @@ def self.to_glob_cache mapped_files.each do |glob, owner| next if owner.nil? + glob_to_owner_map_by_mapper_description.fetch(mapper.description)[glob] = owner end end diff --git a/lib/code_ownership/private.rb b/lib/code_ownership/private.rb index 81119c9..7c9dcee 100644 --- a/lib/code_ownership/private.rb +++ b/lib/code_ownership/private.rb @@ -115,13 +115,11 @@ def self.find_team!(team_name, location_of_reference) sig { returns(GlobCache) } def self.glob_cache @glob_cache ||= T.let(@glob_cache, T.nilable(GlobCache)) - @glob_cache ||= begin - if CodeownersFile.use_codeowners_cache? - CodeownersFile.to_glob_cache - else - Mapper.to_glob_cache - end - end + @glob_cache ||= if CodeownersFile.use_codeowners_cache? + CodeownersFile.to_glob_cache + else + Mapper.to_glob_cache + end end end diff --git a/lib/code_ownership/private/codeowners_file.rb b/lib/code_ownership/private/codeowners_file.rb index 4e744c0..959a6c1 100644 --- a/lib/code_ownership/private/codeowners_file.rb +++ b/lib/code_ownership/private/codeowners_file.rb @@ -13,15 +13,15 @@ class CodeownersFile sig { returns(T::Array[String]) } def self.actual_contents_lines - if !path.exist? - [""] - else + if path.exist? content = path.read lines = path.read.split("\n") if content.end_with?("\n") - lines << "" + lines << '' end lines + else + [''] end end @@ -53,7 +53,7 @@ def self.expected_contents_lines cache.each do |mapper_description, ownership_map_cache| ownership_entries = [] - sorted_ownership_map_cache = ownership_map_cache.sort_by do |glob, team| + sorted_ownership_map_cache = ownership_map_cache.sort_by do |glob, _team| glob end sorted_ownership_map_cache.to_h.each do |path, code_team| @@ -64,22 +64,23 @@ def self.expected_contents_lines # 1) It allows the CODEOWNERS file to be used as a cache for validations # 2) It allows users to specifically see what their team will not be notified about. entry = if ignored_teams.include?(code_team.name) - "# /#{path} #{team_mapping}" - else - "/#{path} #{team_mapping}" - end + "# /#{path} #{team_mapping}" + else + "/#{path} #{team_mapping}" + end ownership_entries << entry end next if ownership_entries.none? + codeowners_file_lines += ['', "# #{mapper_description}", *ownership_entries.sort] end [ *header.split("\n"), - "", # For line between header and codeowners_file_lines + '', # For line between header and codeowners_file_lines *codeowners_file_lines, - "", # For end-of-file newline + '' # For end-of-file newline ] end @@ -123,20 +124,22 @@ def self.to_glob_cache mapper_descriptions = Set.new(Mapper.all.map(&:description)) path.readlines.each do |line| - line_with_no_comment = line.chomp.gsub("# ", "") + line_with_no_comment = line.chomp.gsub('# ', '') if mapper_descriptions.include?(line_with_no_comment) current_mapper = line_with_no_comment else next if current_mapper.nil? - next if line.chomp == "" + next if line.chomp == '' + # The codeowners file stores paths relative to the root of directory # Since a `/` means root of the file system from the perspective of ruby, # we remove that beginning slash so we can correctly glob the files out. - normalized_line = line.gsub(/^# /, '').gsub(/^\//, '') + normalized_line = line.gsub(/^# /, '').gsub(%r{^/}, '') split_line = normalized_line.split # Most lines will be in the format: /path/to/file my-github-team # This will skip over lines that are not of the correct form next if split_line.count > 2 + entry, github_team = split_line code_team = github_team_to_code_team_map[T.must(github_team)] # If a GitHub team is changed and a user runs `bin/codeownership validate`, we won't be able to identify @@ -144,6 +147,7 @@ def self.to_glob_cache # Therefore, if we can't determine the team, we just skip it. # This affects how complete the cache is, but that will still be caught by `bin/codeownership validate`. next if code_team.nil? + raw_cache_contents[current_mapper] ||= {} raw_cache_contents.fetch(current_mapper)[T.must(entry)] = github_team_to_code_team_map.fetch(T.must(github_team)) end diff --git a/lib/code_ownership/private/extension_loader.rb b/lib/code_ownership/private/extension_loader.rb index 39e5c14..2819e73 100644 --- a/lib/code_ownership/private/extension_loader.rb +++ b/lib/code_ownership/private/extension_loader.rb @@ -12,7 +12,7 @@ class << self def load(require_directive) # We want to transform the require directive to behave differently # if it's a specific local file being required versus a gem - if require_directive.start_with?(".") + if require_directive.start_with?('.') require File.join(Pathname.pwd, require_directive) else require require_directive diff --git a/lib/code_ownership/private/glob_cache.rb b/lib/code_ownership/private/glob_cache.rb index 468cdd6..f184f7f 100644 --- a/lib/code_ownership/private/glob_cache.rb +++ b/lib/code_ownership/private/glob_cache.rb @@ -38,7 +38,7 @@ def raw_cache_contents def mapper_descriptions_that_map_files(files) if files.count > EXPANDED_CACHE_FILE_COUNT_THRESHOLD # When looking at many files, expanding the cache out using Dir.glob and checking for intersections is faster - files_by_mappers = files.map{ |f| [f, Set.new([]) ]}.to_h + files_by_mappers = files.to_h { |f| [f, Set.new([])] } files_by_mappers_via_expanded_cache.each do |file, mapper| T.must(files_by_mappers[file]) << mapper if files_by_mappers[file] @@ -68,11 +68,11 @@ def expanded_cache sig { returns(FilesByMapper) } def files_by_mappers_via_expanded_cache - @files_by_mappers ||= T.let(@files_by_mappers, T.nilable(FilesByMapper)) - @files_by_mappers ||= begin + @files_by_mappers_via_expanded_cache ||= T.let(@files_by_mappers_via_expanded_cache, T.nilable(FilesByMapper)) + @files_by_mappers_via_expanded_cache ||= begin files_by_mappers = T.let({}, FilesByMapper) expanded_cache.each do |mapper_description, file_by_owner| - file_by_owner.each do |file, owner| + file_by_owner.each_key do |file| files_by_mappers[file] ||= Set.new([]) files_by_mappers.fetch(file) << mapper_description end @@ -95,7 +95,7 @@ def files_by_mappers_via_file_fnmatch(files) if mapper_description == OwnershipMappers::FileAnnotations::DESCRIPTION files_by_mappers.fetch(file) << mapper_description if globs_by_owner[file] else - globs_by_owner.each do |glob, owner| + globs_by_owner.each_key do |glob| if File.fnmatch?(glob, file, File::FNM_PATHNAME | File::FNM_EXTGLOB) files_by_mappers.fetch(file) << mapper_description end diff --git a/lib/code_ownership/private/ownership_mappers/directory_ownership.rb b/lib/code_ownership/private/ownership_mappers/directory_ownership.rb index e481bfb..2175046 100644 --- a/lib/code_ownership/private/ownership_mappers/directory_ownership.rb +++ b/lib/code_ownership/private/ownership_mappers/directory_ownership.rb @@ -14,8 +14,8 @@ class DirectoryOwnership @@directory_cache = T.let({}, T::Hash[String, T.nilable(CodeTeams::Team)]) # rubocop:disable Style/ClassVars sig do - override.params(file: String). - returns(T.nilable(::CodeTeams::Team)) + override.params(file: String) + .returns(T.nilable(::CodeTeams::Team)) end def map_file_to_owner(file) map_file_to_relevant_owner(file) @@ -37,8 +37,8 @@ def update_cache(cache, files) # subset of files, but rather we want code ownership for all files. # sig do - override.params(files: T::Array[String]). - returns(T::Hash[String, ::CodeTeams::Team]) + override.params(files: T::Array[String]) + .returns(T::Hash[String, ::CodeTeams::Team]) end def globs_to_owner(files) # The T.unsafe is because the upstream RBI is wrong for Pathname.glob @@ -97,7 +97,7 @@ def map_file_to_relevant_owner(file) (path_components.length - 1).downto(0).each do |i| team = get_team_from_codeowners_file_within_directory( Pathname.new(File.join(*T.unsafe(path_components[0...i]))) - ) + ) return team unless team.nil? end @@ -121,7 +121,7 @@ def get_team_from_codeowners_file_within_directory(directory) @@directory_cache[potential_codeowners_file_name] = nil end - return team + team end end end diff --git a/lib/code_ownership/private/ownership_mappers/file_annotations.rb b/lib/code_ownership/private/ownership_mappers/file_annotations.rb index 48ca662..5456fbd 100644 --- a/lib/code_ownership/private/ownership_mappers/file_annotations.rb +++ b/lib/code_ownership/private/ownership_mappers/file_annotations.rb @@ -18,24 +18,24 @@ class FileAnnotations extend T::Sig include Mapper - TEAM_PATTERN = T.let(/\A(?:#|\/\/) @team (?.*)\Z/.freeze, Regexp) + TEAM_PATTERN = T.let(%r{\A(?:#|//) @team (?.*)\Z}.freeze, Regexp) DESCRIPTION = 'Annotations at the top of file' sig do - override.params(file: String). - returns(T.nilable(::CodeTeams::Team)) + override.params(file: String) + .returns(T.nilable(::CodeTeams::Team)) end def map_file_to_owner(file) file_annotation_based_owner(file) end sig do - override. - params(files: T::Array[String]). - returns(T::Hash[String, ::CodeTeams::Team]) + override + .params(files: T::Array[String]) + .returns(T::Hash[String, ::CodeTeams::Team]) end def globs_to_owner(files) - files.each_with_object({}) do |filename_relative_to_root, mapping| # rubocop:disable Style/ClassVars + files.each_with_object({}) do |filename_relative_to_root, mapping| owner = file_annotation_based_owner(filename_relative_to_root) next unless owner @@ -56,10 +56,10 @@ def update_cache(cache, files) invalid_files = cache.keys.select do |file| # If a file is not tracked, it should be removed from the cache !Private.file_tracked?(file) || - # If a file no longer has a file annotation (i.e. `globs_to_owner` doesn't map it) - # it should be removed from the cache - # We make sure to only apply this to the input files since otherwise `updated_cache_for_files.key?(file)` would always return `false` when files == [] - (fileset.include?(file) && !updated_cache_for_files.key?(file)) + # If a file no longer has a file annotation (i.e. `globs_to_owner` doesn't map it) + # it should be removed from the cache + # We make sure to only apply this to the input files since otherwise `updated_cache_for_files.key?(file)` would always return `false` when files == [] + (fileset.include?(file) && !updated_cache_for_files.key?(file)) end invalid_files.each do |invalid_file| @@ -83,14 +83,14 @@ def file_annotation_based_owner(filename) # and if the annotation isn't in the first two lines we assume it # doesn't exist. - line_1 = File.foreach(filename).first + line1 = File.foreach(filename).first - return if !line_1 + return if !line1 begin - team = line_1[TEAM_PATTERN, :team] - rescue ArgumentError => ex - if ex.message.include?('invalid byte sequence') + team = line1[TEAM_PATTERN, :team] + rescue ArgumentError => e + if e.message.include?('invalid byte sequence') team = nil else raise @@ -110,7 +110,7 @@ def remove_file_annotation!(filename) if file_annotation_based_owner(filename) filepath = Pathname.new(filename) lines = filepath.read.split("\n") - new_lines = lines.select { |line| !line[TEAM_PATTERN] } + new_lines = lines.reject { |line| line[TEAM_PATTERN] } # We explicitly add a final new line since splitting by new line when reading the file lines # ignores new lines at the ends of files # We also remove leading new lines, since there is after a new line after an annotation @@ -125,8 +125,7 @@ def description end sig { override.void } - def bust_caches! - end + def bust_caches!; end end end end diff --git a/lib/code_ownership/private/ownership_mappers/js_package_ownership.rb b/lib/code_ownership/private/ownership_mappers/js_package_ownership.rb index 90f9ec7..fc9e5da 100644 --- a/lib/code_ownership/private/ownership_mappers/js_package_ownership.rb +++ b/lib/code_ownership/private/ownership_mappers/js_package_ownership.rb @@ -12,8 +12,8 @@ class JsPackageOwnership @@package_json_cache = T.let({}, T::Hash[String, T.nilable(ParseJsPackages::Package)]) # rubocop:disable Style/ClassVars sig do - override.params(file: String). - returns(T.nilable(::CodeTeams::Team)) + override.params(file: String) + .returns(T.nilable(::CodeTeams::Team)) end def map_file_to_owner(file) package = map_file_to_relevant_package(file) @@ -39,8 +39,8 @@ def update_cache(cache, files) # subset of files, but rather we want code ownership for all files. # sig do - override.params(files: T::Array[String]). - returns(T::Hash[String, ::CodeTeams::Team]) + override.params(files: T::Array[String]) + .returns(T::Hash[String, ::CodeTeams::Team]) end def globs_to_owner(files) ParseJsPackages.all.each_with_object({}) do |package, res| @@ -85,8 +85,8 @@ def map_file_to_relevant_package(file) (path_components.length - 1).downto(0).each do |i| potential_relative_path_name = T.must(path_components[0...i]).reduce(Pathname.new('')) { |built_path, path| built_path.join(path) } - potential_package_json_path = potential_relative_path_name. - join(ParseJsPackages::PACKAGE_JSON_NAME) + potential_package_json_path = potential_relative_path_name + .join(ParseJsPackages::PACKAGE_JSON_NAME) potential_package_json_string = potential_package_json_path.to_s diff --git a/lib/code_ownership/private/ownership_mappers/package_ownership.rb b/lib/code_ownership/private/ownership_mappers/package_ownership.rb index 653f0fc..f26d577 100644 --- a/lib/code_ownership/private/ownership_mappers/package_ownership.rb +++ b/lib/code_ownership/private/ownership_mappers/package_ownership.rb @@ -10,8 +10,8 @@ class PackageOwnership include Mapper sig do - override.params(file: String). - returns(T.nilable(::CodeTeams::Team)) + override.params(file: String) + .returns(T.nilable(::CodeTeams::Team)) end def map_file_to_owner(file) package = Packs.for_file(file) @@ -30,8 +30,8 @@ def map_file_to_owner(file) # subset of files, but rather we want code ownership for all files. # sig do - override.params(files: T::Array[String]). - returns(T::Hash[String, ::CodeTeams::Team]) + override.params(files: T::Array[String]) + .returns(T::Hash[String, ::CodeTeams::Team]) end def globs_to_owner(files) Packs.all.each_with_object({}) do |package, res| @@ -66,8 +66,7 @@ def owner_for_package(package) end sig { override.void } - def bust_caches! - end + def bust_caches!; end end end end diff --git a/lib/code_ownership/private/ownership_mappers/team_globs.rb b/lib/code_ownership/private/ownership_mappers/team_globs.rb index 9daeb46..327b3c2 100644 --- a/lib/code_ownership/private/ownership_mappers/team_globs.rb +++ b/lib/code_ownership/private/ownership_mappers/team_globs.rb @@ -14,11 +14,11 @@ class TeamGlobs @@map_files_to_owners = {} # rubocop:disable Style/ClassVars sig do - params(files: T::Array[String]). - returns(T::Hash[String, ::CodeTeams::Team]) + params(files: T::Array[String]) + .returns(T::Hash[String, ::CodeTeams::Team]) end - def map_files_to_owners(files) # rubocop:disable Lint/UnusedMethodArgument - return @@map_files_to_owners if @@map_files_to_owners&.keys && @@map_files_to_owners.keys.count > 0 + def map_files_to_owners(files) + return @@map_files_to_owners if @@map_files_to_owners&.keys && @@map_files_to_owners.keys.count.positive? @@map_files_to_owners = CodeTeams.all.each_with_object({}) do |team, map| # rubocop:disable Style/ClassVars TeamPlugins::Ownership.for(team).owned_globs.each do |glob| @@ -42,7 +42,7 @@ class GlobOverlap < T::Struct sig { returns(String) } def description # These are sorted only to prevent non-determinism in output between local and CI environments. - sorted_contexts = mapping_contexts.sort_by{|context| context.team.config_yml.to_s } + sorted_contexts = mapping_contexts.sort_by { |context| context.team.config_yml.to_s } description_args = sorted_contexts.map do |context| "`#{context.glob}` (from `#{context.team.config_yml}`)" end @@ -56,7 +56,7 @@ def description end def find_overlapping_globs mapped_files = T.let({}, T::Hash[String, T::Array[MappingContext]]) - CodeTeams.all.each_with_object({}) do |team, map| # rubocop:disable Style/ClassVars + CodeTeams.all.each_with_object({}) do |team, _map| TeamPlugins::Ownership.for(team).owned_globs.each do |glob| Dir.glob(glob).each do |filename| mapped_files[filename] ||= [] @@ -66,24 +66,22 @@ def find_overlapping_globs end overlaps = T.let([], T::Array[GlobOverlap]) - mapped_files.each do |filename, mapping_contexts| + mapped_files.each_value do |mapping_contexts| if mapping_contexts.count > 1 overlaps << GlobOverlap.new(mapping_contexts: mapping_contexts) end end - deduplicated_overlaps = overlaps.uniq do |glob_overlap| + overlaps.uniq do |glob_overlap| glob_overlap.mapping_contexts.map do |context| [context.glob, context.team.name] end end - - deduplicated_overlaps end sig do - override.params(file: String). - returns(T.nilable(::CodeTeams::Team)) + override.params(file: String) + .returns(T.nilable(::CodeTeams::Team)) end def map_file_to_owner(file) map_files_to_owners([file])[file] @@ -97,11 +95,11 @@ def update_cache(cache, files) end sig do - override.params(files: T::Array[String]). - returns(T::Hash[String, ::CodeTeams::Team]) + override.params(files: T::Array[String]) + .returns(T::Hash[String, ::CodeTeams::Team]) end def globs_to_owner(files) - CodeTeams.all.each_with_object({}) do |team, map| # rubocop:disable Style/ClassVars + CodeTeams.all.each_with_object({}) do |team, map| TeamPlugins::Ownership.for(team).owned_globs.each do |owned_glob| map[owned_glob] = team end @@ -128,7 +126,7 @@ def validation_errors(files:, autocorrect: true, stage_changes: true) errors << <<~MSG `owned_globs` cannot overlap between teams. The following globs overlap: - #{overlapping_globs.map { |overlap| "- #{overlap.description}"}.join("\n")} + #{overlapping_globs.map { |overlap| "- #{overlap.description}" }.join("\n")} MSG end diff --git a/lib/code_ownership/private/ownership_mappers/team_yml_ownership.rb b/lib/code_ownership/private/ownership_mappers/team_yml_ownership.rb index 7c86f02..2107150 100644 --- a/lib/code_ownership/private/ownership_mappers/team_yml_ownership.rb +++ b/lib/code_ownership/private/ownership_mappers/team_yml_ownership.rb @@ -13,11 +13,11 @@ class TeamYmlOwnership @@map_files_to_owners = {} # rubocop:disable Style/ClassVars sig do - params(files: T::Array[String]). - returns(T::Hash[String, ::CodeTeams::Team]) + params(files: T::Array[String]) + .returns(T::Hash[String, ::CodeTeams::Team]) end - def map_files_to_owners(files) # rubocop:disable Lint/UnusedMethodArgument - return @@map_files_to_owners if @@map_files_to_owners&.keys && @@map_files_to_owners.keys.count > 0 + def map_files_to_owners(files) + return @@map_files_to_owners if @@map_files_to_owners&.keys && @@map_files_to_owners.keys.count.positive? @@map_files_to_owners = CodeTeams.all.each_with_object({}) do |team, map| # rubocop:disable Style/ClassVars map[team.config_yml] = team @@ -25,19 +25,19 @@ def map_files_to_owners(files) # rubocop:disable Lint/UnusedMethodArgument end sig do - override.params(file: String). - returns(T.nilable(::CodeTeams::Team)) + override.params(file: String) + .returns(T.nilable(::CodeTeams::Team)) end def map_file_to_owner(file) map_files_to_owners([file])[file] end sig do - override.params(files: T::Array[String]). - returns(T::Hash[String, ::CodeTeams::Team]) + override.params(files: T::Array[String]) + .returns(T::Hash[String, ::CodeTeams::Team]) end def globs_to_owner(files) - CodeTeams.all.each_with_object({}) do |team, map| # rubocop:disable Style/ClassVars + CodeTeams.all.each_with_object({}) do |team, map| map[team.config_yml] = team end end diff --git a/lib/code_ownership/private/parse_js_packages.rb b/lib/code_ownership/private/parse_js_packages.rb index ce2eb2b..caf0913 100644 --- a/lib/code_ownership/private/parse_js_packages.rb +++ b/lib/code_ownership/private/parse_js_packages.rb @@ -23,10 +23,10 @@ def self.from(pathname) package_loaded_json = JSON.parse(pathname.read) package_name = if pathname.dirname == Pathname.new('.') - ROOT_PACKAGE_NAME - else - pathname.dirname.cleanpath.to_s - end + ROOT_PACKAGE_NAME + else + pathname.dirname.cleanpath.to_s + end new( name: package_name, @@ -41,7 +41,7 @@ def self.from(pathname) Please either make the JSON in that file valid or specify `js_package_paths` in config/code_ownership.yml. MESSAGE - raise InvalidCodeOwnershipConfigurationError.new(error_message) + raise InvalidCodeOwnershipConfigurationError, error_message end sig { returns(Pathname) } diff --git a/lib/code_ownership/private/permit_pack_owner_top_level_key.rb b/lib/code_ownership/private/permit_pack_owner_top_level_key.rb index 2188113..c5b7b89 100644 --- a/lib/code_ownership/private/permit_pack_owner_top_level_key.rb +++ b/lib/code_ownership/private/permit_pack_owner_top_level_key.rb @@ -1,4 +1,3 @@ - # typed: strict # frozen_string_literal: true diff --git a/lib/code_ownership/private/validations/files_have_owners.rb b/lib/code_ownership/private/validations/files_have_owners.rb index 0a09419..221dc96 100644 --- a/lib/code_ownership/private/validations/files_have_owners.rb +++ b/lib/code_ownership/private/validations/files_have_owners.rb @@ -12,8 +12,8 @@ class FilesHaveOwners def validation_errors(files:, autocorrect: true, stage_changes: true) cache = Private.glob_cache file_mappings = cache.mapper_descriptions_that_map_files(files) - files_not_mapped_at_all = file_mappings.select do |file, mapper_descriptions| - mapper_descriptions.count == 0 + files_not_mapped_at_all = file_mappings.select do |_file, mapper_descriptions| + mapper_descriptions.count.zero? end errors = T.let([], T::Array[String]) @@ -22,7 +22,7 @@ def validation_errors(files:, autocorrect: true, stage_changes: true) errors << <<~MSG Some files are missing ownership: - #{files_not_mapped_at_all.map { |file, mappers| "- #{file}" }.join("\n")} + #{files_not_mapped_at_all.map { |file, _mappers| "- #{file}" }.join("\n")} MSG end diff --git a/lib/code_ownership/private/validations/files_have_unique_owners.rb b/lib/code_ownership/private/validations/files_have_unique_owners.rb index 776cf16..567fa2b 100644 --- a/lib/code_ownership/private/validations/files_have_unique_owners.rb +++ b/lib/code_ownership/private/validations/files_have_unique_owners.rb @@ -12,7 +12,7 @@ class FilesHaveUniqueOwners def validation_errors(files:, autocorrect: true, stage_changes: true) cache = Private.glob_cache file_mappings = cache.mapper_descriptions_that_map_files(files) - files_mapped_by_multiple_mappers = file_mappings.select do |file, mapper_descriptions| + files_mapped_by_multiple_mappers = file_mappings.select do |_file, mapper_descriptions| mapper_descriptions.count > 1 end diff --git a/lib/code_ownership/private/validations/github_codeowners_up_to_date.rb b/lib/code_ownership/private/validations/github_codeowners_up_to_date.rb index 8ebf67c..88061cc 100644 --- a/lib/code_ownership/private/validations/github_codeowners_up_to_date.rb +++ b/lib/code_ownership/private/validations/github_codeowners_up_to_date.rb @@ -25,7 +25,7 @@ def validation_errors(files:, autocorrect: true, stage_changes: true) `git add #{CodeownersFile.path}` end # If there is no current file or its empty, display a shorter message. - elsif actual_content_lines == [""] + elsif actual_content_lines == [''] errors << <<~CODEOWNERS_ERROR CODEOWNERS out of date. Run `bin/codeownership validate` to update the CODEOWNERS file CODEOWNERS_ERROR @@ -34,33 +34,33 @@ def validation_errors(files:, autocorrect: true, stage_changes: true) extra_lines = actual_content_lines - expected_content_lines missing_lines_text = if missing_lines.any? - <<~COMMENT - CODEOWNERS should contain the following lines, but does not: - #{(missing_lines).map { |line| "- \"#{line}\""}.join("\n")} - COMMENT - end + <<~COMMENT + CODEOWNERS should contain the following lines, but does not: + #{missing_lines.map { |line| "- \"#{line}\"" }.join("\n")} + COMMENT + end extra_lines_text = if extra_lines.any? - <<~COMMENT - CODEOWNERS should not contain the following lines, but it does: - #{(extra_lines).map { |line| "- \"#{line}\""}.join("\n")} - COMMENT - end + <<~COMMENT + CODEOWNERS should not contain the following lines, but it does: + #{extra_lines.map { |line| "- \"#{line}\"" }.join("\n")} + COMMENT + end diff_text = if missing_lines_text && extra_lines_text - "#{missing_lines_text}\n#{extra_lines_text}".chomp - elsif missing_lines_text - missing_lines_text - elsif extra_lines_text - extra_lines_text - else - <<~TEXT - There may be extra lines, or lines are out of order. - You can try to regenerate the CODEOWNERS file from scratch: - 1) `rm .github/CODEOWNERS` - 2) `bin/codeownership validate` - TEXT - end + "#{missing_lines_text}\n#{extra_lines_text}".chomp + elsif missing_lines_text + missing_lines_text + elsif extra_lines_text + extra_lines_text + else + <<~TEXT + There may be extra lines, or lines are out of order. + You can try to regenerate the CODEOWNERS file from scratch: + 1) `rm .github/CODEOWNERS` + 2) `bin/codeownership validate` + TEXT + end errors << <<~CODEOWNERS_ERROR CODEOWNERS out of date. Run `bin/codeownership validate` to update the CODEOWNERS file diff --git a/lib/code_ownership/validator.rb b/lib/code_ownership/validator.rb index 42e91aa..e93a914 100644 --- a/lib/code_ownership/validator.rb +++ b/lib/code_ownership/validator.rb @@ -8,8 +8,7 @@ module Validator interface! sig { abstract.params(files: T::Array[String], autocorrect: T::Boolean, stage_changes: T::Boolean).returns(T::Array[String]) } - def validation_errors(files:, autocorrect: true, stage_changes: true) - end + def validation_errors(files:, autocorrect: true, stage_changes: true); end class << self extend T::Sig diff --git a/spec/lib/code_ownership/cli_spec.rb b/spec/lib/code_ownership/cli_spec.rb index ed7323c..fcfe136 100644 --- a/spec/lib/code_ownership/cli_spec.rb +++ b/spec/lib/code_ownership/cli_spec.rb @@ -13,7 +13,7 @@ context 'when run without arguments' do it 'runs validations with the right defaults' do - expect(CodeOwnership).to receive(:validate!) do |args| # rubocop:disable RSpec/MessageSpies + expect(CodeOwnership).to receive(:validate!) do |args| expect(args[:autocorrect]).to eq true expect(args[:stage_changes]).to eq true expect(args[:files]).to be_nil @@ -47,7 +47,7 @@ write_file('app/services/my_file.rb') write_file('config/teams/my_team.yml', <<~YML) name: My Team - owned_globs: + owned_globs:#{' '} - 'app/**/*.rb' YML end @@ -55,7 +55,7 @@ context 'when run with no flags' do context 'when run with one file' do let(:argv) { ['for_file', 'app/services/my_file.rb'] } - + it 'outputs the team info in human readable format' do expect(CodeOwnership::Cli).to receive(:puts).with(<<~MSG) Team: My Team @@ -67,17 +67,17 @@ context 'when run with no files' do let(:argv) { ['for_file'] } - + it 'outputs the team info in human readable format' do - expect { subject }.to raise_error "Please pass in one file. Use `bin/codeownership for_file --help` for more info" + expect { subject }.to raise_error 'Please pass in one file. Use `bin/codeownership for_file --help` for more info' end end context 'when run with multiple files' do let(:argv) { ['for_file', 'app/services/my_file.rb', 'app/services/my_file2.rb'] } - + it 'outputs the team info in human readable format' do - expect { subject }.to raise_error "Please pass in one file. Use `bin/codeownership for_file --help` for more info" + expect { subject }.to raise_error 'Please pass in one file. Use `bin/codeownership for_file --help` for more info' end end end @@ -98,17 +98,17 @@ context 'when run with no files' do let(:argv) { ['for_file', '--json'] } - + it 'outputs the team info in human readable format' do - expect { subject }.to raise_error "Please pass in one file. Use `bin/codeownership for_file --help` for more info" + expect { subject }.to raise_error 'Please pass in one file. Use `bin/codeownership for_file --help` for more info' end end context 'when run with multiple files' do let(:argv) { ['for_file', 'app/services/my_file.rb', 'app/services/my_file2.rb'] } - + it 'outputs the team info in human readable format' do - expect { subject }.to raise_error "Please pass in one file. Use `bin/codeownership for_file --help` for more info" + expect { subject }.to raise_error 'Please pass in one file. Use `bin/codeownership for_file --help` for more info' end end end @@ -128,13 +128,13 @@ it 'outputs help text' do expected = <<~EXPECTED - Usage: bin/codeownership + Usage: bin/codeownership - Subcommands: - validate - run all validations - for_file - find code ownership for a single file - for_team - find code ownership information for a team - help - display help information about code_ownership + Subcommands: + validate - run all validations + for_file - find code ownership for a single file + for_team - find code ownership information for a team + help - display help information about code_ownership EXPECTED expect(CodeOwnership::Cli).to receive(:puts).with(expected) subject diff --git a/spec/lib/code_ownership/private/extension_loader_spec.rb b/spec/lib/code_ownership/private/extension_loader_spec.rb index 07325a0..ce75af8 100644 --- a/spec/lib/code_ownership/private/extension_loader_spec.rb +++ b/spec/lib/code_ownership/private/extension_loader_spec.rb @@ -53,14 +53,14 @@ def bust_caches! end RUBY - expect_any_instance_of(codeowners_validation).to receive(:`).with("git add #{codeowners_path}") # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to receive(:`).with("git add #{codeowners_path}") end after(:all) do - validators_without_extension = Validator.instance_variable_get(:@validators).reject{|v| v == MyExtension } + validators_without_extension = Validator.instance_variable_get(:@validators).reject { |v| v == MyExtension } Validator.instance_variable_set(:@validators, validators_without_extension) - mappers_without_extension = Mapper.instance_variable_get(:@mappers).reject{|v| v == MyExtension } - Mapper.instance_variable_set(:@mappers, mappers_without_extension) + mappers_without_extension = Mapper.instance_variable_get(:@mappers).reject { |v| v == MyExtension } + Mapper.instance_variable_set(:@mappers, mappers_without_extension) end describe 'CodeOwnership.validate!' do diff --git a/spec/lib/code_ownership/private/owner_assigner_spec.rb b/spec/lib/code_ownership/private/owner_assigner_spec.rb index 80e91fa..b47ad6e 100644 --- a/spec/lib/code_ownership/private/owner_assigner_spec.rb +++ b/spec/lib/code_ownership/private/owner_assigner_spec.rb @@ -3,20 +3,20 @@ module CodeOwnership describe '.assign_owners' do subject(:assign_owners) { described_class.assign_owners(globs_to_owning_team_map) } - let(:team_1) { instance_double(CodeTeams::Team) } - let(:team_2) { instance_double(CodeTeams::Team) } + let(:team1) { instance_double(CodeTeams::Team) } + let(:team2) { instance_double(CodeTeams::Team) } let(:globs_to_owning_team_map) do { - 'app/services/[test]/some_other_file.ts' => team_1, - 'app/services/withoutbracket/file.ts' => team_2, - 'app/models/*.rb' => team_2 + 'app/services/[test]/some_other_file.ts' => team1, + 'app/services/withoutbracket/file.ts' => team2, + 'app/models/*.rb' => team2 } end before do write_file('app/services/[test]/some_other_file.ts', <<~YML) - // @team Bar + // @team Bar YML write_file('app/services/withoutbracket/file.ts', <<~YML) @@ -26,32 +26,32 @@ module CodeOwnership it 'returns a hash with the same keys and the values that are files' do expect(assign_owners).to eq( - 'app/services/[test]/some_other_file.ts' => team_1, - 'app/services/withoutbracket/file.ts' => team_2 + 'app/services/[test]/some_other_file.ts' => team1, + 'app/services/withoutbracket/file.ts' => team2 ) end context 'when file name includes square brackets' do let(:globs_to_owning_team_map) do { - 'app/services/[test]/some_other_[test]_file.ts' => team_1, + 'app/services/[test]/some_other_[test]_file.ts' => team1 } end before do write_file('app/services/[test]/some_other_[test]_file.ts', <<~YML) - // @team Bar + // @team Bar YML write_file('app/services/t/some_other_e_file.ts', <<~YML) - // @team Bar + // @team Bar YML end it 'matches the glob pattern' do expect(assign_owners).to eq( - 'app/services/[test]/some_other_[test]_file.ts' => team_1, - 'app/services/t/some_other_e_file.ts' => team_1 + 'app/services/[test]/some_other_[test]_file.ts' => team1, + 'app/services/t/some_other_e_file.ts' => team1 ) end end @@ -59,15 +59,15 @@ module CodeOwnership context 'when glob pattern also exists' do before do write_file('app/services/t/some_other_file.ts', <<~YML) - // @team Bar + // @team Bar YML end it 'also matches the glob pattern' do expect(assign_owners).to eq( - 'app/services/[test]/some_other_file.ts' => team_1, - 'app/services/t/some_other_file.ts' => team_1, - 'app/services/withoutbracket/file.ts' => team_2 + 'app/services/[test]/some_other_file.ts' => team1, + 'app/services/t/some_other_file.ts' => team1, + 'app/services/withoutbracket/file.ts' => team2 ) end end @@ -75,19 +75,19 @@ module CodeOwnership context 'when * is used in glob pattern' do before do write_file('app/models/some_file.rb', <<~YML) - // @team Bar + // @team Bar YML write_file('app/models/nested/some_file.rb', <<~YML) - // @team Bar + // @team Bar YML end it 'also matches the glob pattern' do expect(assign_owners).to eq( - 'app/services/[test]/some_other_file.ts' => team_1, - 'app/services/withoutbracket/file.ts' => team_2, - 'app/models/some_file.rb' => team_2 + 'app/services/[test]/some_other_file.ts' => team1, + 'app/services/withoutbracket/file.ts' => team2, + 'app/models/some_file.rb' => team2 ) end end diff --git a/spec/lib/code_ownership/private/ownership_mappers/file_annotations_spec.rb b/spec/lib/code_ownership/private/ownership_mappers/file_annotations_spec.rb index 355eba1..2758cf5 100644 --- a/spec/lib/code_ownership/private/ownership_mappers/file_annotations_spec.rb +++ b/spec/lib/code_ownership/private/ownership_mappers/file_annotations_spec.rb @@ -217,6 +217,5 @@ module CodeOwnership end end end - end end diff --git a/spec/lib/code_ownership/private/ownership_mappers/js_package_ownership_spec.rb b/spec/lib/code_ownership/private/ownership_mappers/js_package_ownership_spec.rb index 097ef72..820b389 100644 --- a/spec/lib/code_ownership/private/ownership_mappers/js_package_ownership_spec.rb +++ b/spec/lib/code_ownership/private/ownership_mappers/js_package_ownership_spec.rb @@ -17,7 +17,7 @@ module CodeOwnership it 'lets the user know the their package JSON is invalid' do expect { CodeOwnership.validate! }.to raise_error do |e| expect(e).to be_a CodeOwnership::InvalidCodeOwnershipConfigurationError - expect(e.message).to match /JSON::ParserError.*?unexpected token/ + expect(e.message).to match(/JSON::ParserError.*?unexpected token/) expect(e.message).to include 'frontend/javascripts/my_package/package.json has invalid JSON, so code ownership cannot be determined.' expect(e.message).to include 'Please either make the JSON in that file valid or specify `js_package_paths` in config/code_ownership.yml.' end diff --git a/spec/lib/code_ownership/private/validations/files_have_owners_spec.rb b/spec/lib/code_ownership/private/validations/files_have_owners_spec.rb index a0b766f..213eb51 100644 --- a/spec/lib/code_ownership/private/validations/files_have_owners_spec.rb +++ b/spec/lib/code_ownership/private/validations/files_have_owners_spec.rb @@ -5,8 +5,7 @@ module CodeOwnership context 'input files are not part of configured owned_globs' do before do - write_file('Gemfile', <<~CONTENTS) - CONTENTS + write_file('Gemfile', '') write_configuration end @@ -18,8 +17,7 @@ module CodeOwnership context 'a file in owned_globs does not have an owner' do before do - write_file('app/missing_ownership.rb', <<~CONTENTS) - CONTENTS + write_file('app/missing_ownership.rb', '') write_file('app/some_other_file.rb', <<~CONTENTS) # @team Bar @@ -71,15 +69,14 @@ module CodeOwnership write_configuration 500.times do |i| - write_file("app/missing_ownership#{i}.rb", <<~CONTENTS) - CONTENTS + write_file("app/missing_ownership#{i}.rb", '') end end it 'lets the user know the file must have ownership' do expect { CodeOwnership.validate! }.to raise_error do |e| expect(e).to be_a CodeOwnership::InvalidCodeOwnershipConfigurationError - expect(e.message).to include "Some files are missing ownership:" + expect(e.message).to include 'Some files are missing ownership:' 500.times do |i| expect(e.message).to include "- app/missing_ownership#{i}.rb" end diff --git a/spec/lib/code_ownership/private/validations/github_codeowners_up_to_date_spec.rb b/spec/lib/code_ownership/private/validations/github_codeowners_up_to_date_spec.rb index da284e7..905be8d 100644 --- a/spec/lib/code_ownership/private/validations/github_codeowners_up_to_date_spec.rb +++ b/spec/lib/code_ownership/private/validations/github_codeowners_up_to_date_spec.rb @@ -11,7 +11,7 @@ module CodeOwnership context 'in an empty application' do it 'automatically regenerates the codeowners file' do expect(codeowners_path).to_not exist - expect_any_instance_of(codeowners_validation).to receive(:`).with("git add #{codeowners_path}") # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to receive(:`).with("git add #{codeowners_path}") expect { CodeOwnership.validate! }.to_not raise_error expect(codeowners_path.read).to eq <<~EXPECTED # STOP! - DO NOT EDIT THIS FILE MANUALLY @@ -31,7 +31,7 @@ module CodeOwnership it 'automatically regenerates the codeowners file' do expect(codeowners_path).to_not exist - expect_any_instance_of(codeowners_validation).to receive(:`).with("git add #{codeowners_path}") # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to receive(:`).with("git add #{codeowners_path}") expect { CodeOwnership.validate! }.to_not raise_error expect(codeowners_path.read).to eq <<~EXPECTED # STOP! - DO NOT EDIT THIS FILE MANUALLY @@ -68,7 +68,7 @@ module CodeOwnership context 'the user has passed in specific input files into the validate method' do it 'still automatically regenerates the codeowners file, since we look at all files when regenerating CODEOWNERS' do expect(codeowners_path).to_not exist - expect_any_instance_of(codeowners_validation).to receive(:`).with("git add #{codeowners_path}") # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to receive(:`).with("git add #{codeowners_path}") expect { CodeOwnership.validate! }.to_not raise_error expect(codeowners_path.read).to eq <<~EXPECTED # STOP! - DO NOT EDIT THIS FILE MANUALLY @@ -116,7 +116,7 @@ module CodeOwnership it 'does not include the team in the output' do expect(codeowners_path).to_not exist expect { CodeOwnership.validate! }.to_not raise_error - expect_any_instance_of(codeowners_validation).to_not receive(:`) # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to_not receive(:`) expect(codeowners_path.read).to eq <<~EXPECTED # STOP! - DO NOT EDIT THIS FILE MANUALLY # This file was automatically generated by "bin/codeownership validate". @@ -146,7 +146,7 @@ module CodeOwnership it 'does not include the team in the output' do expect(codeowners_path).to_not exist expect { CodeOwnership.validate! }.to_not raise_error - expect_any_instance_of(codeowners_validation).to_not receive(:`) # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to_not receive(:`) expect(codeowners_path.read).to eq <<~EXPECTED # STOP! - DO NOT EDIT THIS FILE MANUALLY # This file was automatically generated by "bin/codeownership validate". @@ -188,7 +188,7 @@ module CodeOwnership it 'does not stage the changes to the codeowners file' do expect(codeowners_path).to_not exist - expect_any_instance_of(codeowners_validation).to_not receive(:`) # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to_not receive(:`) expect { CodeOwnership.validate!(stage_changes: false) }.to_not raise_error expect(codeowners_path.read).to eq <<~EXPECTED # STOP! - DO NOT EDIT THIS FILE MANUALLY @@ -212,7 +212,7 @@ module CodeOwnership context 'in an empty application' do it 'automatically regenerates the codeowners file' do expect(codeowners_path).to_not exist - expect_any_instance_of(codeowners_validation).to_not receive(:`) # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to_not receive(:`) expect { CodeOwnership.validate!(autocorrect: false) }.to raise_error do |e| expect(e).to be_a CodeOwnership::InvalidCodeOwnershipConfigurationError expect(e.message).to eq <<~EXPECTED.chomp @@ -230,7 +230,7 @@ module CodeOwnership it 'automatically regenerates the codeowners file' do expect(codeowners_path).to_not exist - expect_any_instance_of(codeowners_validation).to_not receive(:`) # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to_not receive(:`) expect { CodeOwnership.validate!(autocorrect: false) }.to raise_error do |e| expect(e).to be_a CodeOwnership::InvalidCodeOwnershipConfigurationError expect(e.message).to eq <<~EXPECTED.chomp @@ -254,7 +254,7 @@ module CodeOwnership it 'does not include the team in the output' do expect(codeowners_path).to_not exist - expect_any_instance_of(codeowners_validation).to_not receive(:`) # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to_not receive(:`) expect { CodeOwnership.validate!(autocorrect: false) }.to raise_error do |e| expect(e).to be_a CodeOwnership::InvalidCodeOwnershipConfigurationError expect(e.message).to eq <<~EXPECTED.chomp @@ -282,7 +282,7 @@ module CodeOwnership it 'does not include the team in the output' do expect(codeowners_path).to_not exist - expect_any_instance_of(codeowners_validation).to_not receive(:`) # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to_not receive(:`) expect { CodeOwnership.validate!(autocorrect: false) }.to raise_error do |e| expect(e).to be_a CodeOwnership::InvalidCodeOwnershipConfigurationError expect(e.message).to eq <<~EXPECTED.chomp @@ -328,7 +328,7 @@ module CodeOwnership /config/teams/bar.yml @MyOrg/bar-team CODEOWNERS - expect_any_instance_of(codeowners_validation).to_not receive(:`) # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to_not receive(:`) expect { CodeOwnership.validate!(autocorrect: false) }.to raise_error do |e| expect(e).to be_a CodeOwnership::InvalidCodeOwnershipConfigurationError expect(e.message).to eq <<~EXPECTED.chomp @@ -390,7 +390,7 @@ module CodeOwnership /config/teams/bar.yml @MyOrg/bar-team CODEOWNERS - expect_any_instance_of(codeowners_validation).to_not receive(:`) # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to_not receive(:`) expect { CodeOwnership.validate!(autocorrect: false) }.to raise_error do |e| expect(e).to be_a CodeOwnership::InvalidCodeOwnershipConfigurationError expect(e.message).to eq <<~EXPECTED.chomp @@ -435,7 +435,7 @@ module CodeOwnership /config/teams/bar.yml @MyOrg/bar-team CODEOWNERS - expect_any_instance_of(codeowners_validation).to_not receive(:`) # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to_not receive(:`) expect { CodeOwnership.validate!(autocorrect: false) }.to raise_error do |e| expect(e).to be_a CodeOwnership::InvalidCodeOwnershipConfigurationError expect(e.message).to eq <<~EXPECTED.chomp @@ -490,7 +490,7 @@ module CodeOwnership /config/teams/bar.yml @MyOrg/bar-team CODEOWNERS - expect_any_instance_of(codeowners_validation).to_not receive(:`) # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to_not receive(:`) expect { CodeOwnership.validate!(autocorrect: false) }.to_not raise_error end end @@ -531,7 +531,7 @@ module CodeOwnership /config/teams/bar.yml @MyOrg/bar-team CODEOWNERS - expect_any_instance_of(codeowners_validation).to_not receive(:`) # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to_not receive(:`) expect { CodeOwnership.validate!(autocorrect: false) }.to raise_error do |e| expect(e).to be_a CodeOwnership::InvalidCodeOwnershipConfigurationError expect(e.message).to eq <<~EXPECTED.chomp @@ -579,7 +579,7 @@ module CodeOwnership /packs/my_pack/owned_file.rb @MyOrg/this-team-does-not-exist CODEOWNERS - expect_any_instance_of(codeowners_validation).to_not receive(:`) # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to_not receive(:`) expect { CodeOwnership.validate!(autocorrect: false) }.to raise_error do |e| expect(e).to be_a CodeOwnership::InvalidCodeOwnershipConfigurationError expect(e.message).to eq <<~EXPECTED.chomp @@ -631,7 +631,7 @@ module CodeOwnership /packs/my_pack/deleted_file.rb @MyOrg/bar-team CODEOWNERS - expect_any_instance_of(codeowners_validation).to_not receive(:`) # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to_not receive(:`) expect { CodeOwnership.validate!(autocorrect: false) }.to raise_error do |e| expect(e).to be_a CodeOwnership::InvalidCodeOwnershipConfigurationError expect(e.message).to eq <<~EXPECTED.chomp @@ -654,8 +654,7 @@ module CodeOwnership before do write_configuration - write_file('packs/my_pack/had_annotation_file.rb', <<~CONTENTS) - CONTENTS + write_file('packs/my_pack/had_annotation_file.rb', '') write_file('config/teams/bar.yml', <<~CONTENTS) name: Bar @@ -682,7 +681,7 @@ module CodeOwnership /config/teams/bar.yml @MyOrg/bar-team CODEOWNERS - expect_any_instance_of(codeowners_validation).to_not receive(:`) # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to_not receive(:`) expect { CodeOwnership.validate!(autocorrect: false) }.to raise_error do |e| expect(e).to be_a CodeOwnership::InvalidCodeOwnershipConfigurationError expect(e.message).to eq <<~EXPECTED.chomp @@ -736,7 +735,7 @@ module CodeOwnership /config/teams/bar.yml @MyOrg/bar-team CODEOWNERS - expect_any_instance_of(codeowners_validation).to_not receive(:`) # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to_not receive(:`) expect { CodeOwnership.validate!(autocorrect: false, files: []) }.to_not raise_error end end @@ -749,7 +748,7 @@ module CodeOwnership it 'skips validating the codeowners file' do expect(codeowners_path).to_not exist - expect_any_instance_of(codeowners_validation).to_not receive(:`) # rubocop:disable RSpec/AnyInstance + expect_any_instance_of(codeowners_validation).to_not receive(:`) expect { CodeOwnership.validate!(autocorrect: false) }.to_not raise_error expect(codeowners_path).to_not exist end @@ -776,9 +775,9 @@ module CodeOwnership it 'expect code teams validations to fail' do expect(CodeTeams.validation_errors(CodeTeams.all)).to eq([ - "More than 1 definition for Bar found", - "The following teams are specified multiple times:\nEach code team must have a unique GitHub team in order to write the CODEOWNERS file correctly.\n\n@MyOrg/bar-team\n" - ]) + 'More than 1 definition for Bar found', + "The following teams are specified multiple times:\nEach code team must have a unique GitHub team in order to write the CODEOWNERS file correctly.\n\n@MyOrg/bar-team\n" + ]) end end @@ -797,8 +796,8 @@ module CodeOwnership it 'does not report CodeTeams without github.teams key' do expect(CodeTeams.validation_errors(CodeTeams.all)).to eq([ - "More than 1 definition for Bar found" - ]) + 'More than 1 definition for Bar found' + ]) end end end @@ -822,9 +821,9 @@ module CodeOwnership it 'reports CodeTeams without github.team keys' do errors = CodeTeams.validation_errors(CodeTeams.all) expect(errors.length).to eq(1) - expect(errors.first).to include("The following teams are missing `github.team` entries:") - expect(errors.first).to include("config/teams/bar.yml") - expect(errors.first).to include("config/teams/foo.yml") + expect(errors.first).to include('The following teams are missing `github.team` entries:') + expect(errors.first).to include('config/teams/bar.yml') + expect(errors.first).to include('config/teams/foo.yml') end end diff --git a/spec/lib/code_ownership_spec.rb b/spec/lib/code_ownership_spec.rb index d053895..ae5b70a 100644 --- a/spec/lib/code_ownership_spec.rb +++ b/spec/lib/code_ownership_spec.rb @@ -28,8 +28,8 @@ context 'file ownership with [] characters' do before do write_file('app/services/[test]/some_other_file.ts', <<~YML) - // @team Bar - // Countries + // @team Bar + // Countries YML 100.times do |i| @@ -168,7 +168,6 @@ end before { create_non_empty_application } - end describe '.for_backtrace' do @@ -201,7 +200,6 @@ create_files_with_defined_classes end - context 'excluded_teams is not passed in as an input parameter' do it 'finds the right team' do expect { MyFile.raise_error }.to raise_error do |ex| diff --git a/spec/support/application_fixtures.rb b/spec/support/application_fixtures.rb index 06c1bab..9b553f2 100644 --- a/spec/support/application_fixtures.rb +++ b/spec/support/application_fixtures.rb @@ -1,7 +1,6 @@ RSpec.shared_context 'application fixtures' do let(:codeowners_path) { Pathname.pwd.join('.github/CODEOWNERS') } - def write_configuration(owned_globs: nil, **kwargs) owned_globs ||= ['{app,components,config,frontend,lib,packs,spec}/**/*.{rb,rake,js,jsx,ts,tsx,json,yml}'] config = { @@ -63,7 +62,6 @@ def write_configuration(owned_globs: nil, **kwargs) write_file('packs/my_other_package/my_file.rb') end - let(:create_files_with_defined_classes) do write_file('app/my_file.rb', <<~CONTENTS) # @team Foo @@ -98,8 +96,8 @@ def self.raise_error # Some of the tests use the `SequoiaTree` constant. Since the implementation leverages: # `path = Object.const_source_location(klass.to_s)&.first`, we want to make sure that # we re-require the constant each time, since `RSpecTempfiles` changes where the file lives with each test - Object.send(:remove_const, :MyFile) if defined? MyFile # rubocop:disable Style/Send: - Object.send(:remove_const, :MyError) if defined? MyError # rubocop:disable Style/Send: + Object.send(:remove_const, :MyFile) if defined? MyFile # : + Object.send(:remove_const, :MyError) if defined? MyError # : require Pathname.pwd.join('app/my_file') end end