# typed: strict # frozen_string_literal: true module CodeOwnership module Private # # This class is responsible for turning CodeOwnership directives (e.g. annotations, package owners) # into a GitHub CODEOWNERS file, as specified here: # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners # class CodeownersFile extend T::Sig sig { returns(T::Array[String]) } def self.actual_contents_lines if !path.exist? [""] else content = path.read lines = path.read.split("\n") if content.end_with?("\n") lines << "" end lines end end sig { returns(T::Array[T.nilable(String)]) } def self.expected_contents_lines cache = Private.glob_cache.raw_cache_contents header = <<~HEADER # STOP! - DO NOT EDIT THIS FILE MANUALLY # This file was automatically generated by "bin/codeownership validate". # # CODEOWNERS is used for GitHub to suggest code/file owners to various GitHub # teams. This is useful when developers create Pull Requests since the # code/file owner is notified. Reference GitHub docs for more details: # https://help.github.com/en/articles/about-code-owners HEADER ignored_teams = T.let(Set.new, T::Set[String]) github_team_map = CodeTeams.all.each_with_object({}) do |team, map| team_github = TeamPlugins::Github.for(team).github if team_github.do_not_add_to_codeowners_file ignored_teams << team.name end map[team.name] = team_github.team end codeowners_file_lines = T.let([], T::Array[String]) cache.each do |mapper_description, ownership_map_cache| ownership_entries = [] ownership_map_cache.each do |path, code_team| team_mapping = github_team_map[code_team.name] next if team_mapping.nil? next if ignored_teams.include?(code_team.name) entry = "/#{path} #{team_mapping}" # In order to use the codeowners file as a proper cache, we'll need to insert commented out entries for ignored teams # entry = if ignored_teams.include?(code_team.name) # "# /#{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 *codeowners_file_lines, "", # For end-of-file newline ] end sig { void } def self.write! FileUtils.mkdir_p(path.dirname) if !path.dirname.exist? path.write(expected_contents_lines.join("\n")) end sig { returns(Pathname) } def self.path Pathname.pwd.join('.github/CODEOWNERS') end end end end