# frozen_string_literal: true module CobraCommander # Calculates directly & transitively affected components class Affected def initialize(umbrella, changes) @umbrella = umbrella @changes = changes run! end def names @names ||= all_affected.map(&:name) end def scripts @scripts ||= paths.map { |path| File.join(path, "test.sh") } end def directly @directly.map(&method(:affected_component)) end def transitively @transitively.map(&method(:affected_component)) end def json_representation # rubocop:disable Metrics/MethodLength { changed_files: @changes, directly_affected_components: directly, transitively_affected_components: transitively, test_scripts: scripts, component_names: names, languages: { ruby: contains_ruby?, javascript: contains_js?, }, }.to_json end private def run! @transitively = Set.new @directly = Set.new @umbrella.components.each(&method(:add_if_changed)) @transitively = @transitively.sort_by(&:name) @directly = @directly.sort_by(&:name) end def add_if_changed(component) return if component.root_paths.uniq.none? { |path| @changes.any?(Regexp.new(path)) } @directly << component @transitively.merge(component.deep_dependents) end def affected_component(component) { name: component.name, path: component.root_paths, type: component.sources.keys.map(&:to_s).map(&:capitalize).join(" & "), } end def all_affected @all_affected ||= (@directly | @transitively).sort_by(&:name) end def paths @paths ||= all_affected.map(&:root_paths).flatten.uniq end def all_affected_sources all_affected .map(&:sources) .map(&:keys) .flatten .uniq end def contains_ruby? all_affected_sources.include?(:bundler) end def contains_js? all_affected_sources.include?(:yarn) end end end