# 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 all_affected @all_affected ||= (@directly | @transitively).sort_by(&: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 { 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 component_changed?(component) component.root_paths.any? do |component_path| @changes.any? do |file_path| file_path.start_with?(component_path) end end end def add_if_changed(component) return unless component_changed?(component) @directly << component @transitively.merge(component.deep_dependents) end def affected_component(component) { name: component.name, path: component.root_paths, type: component.packages.keys.map(&:to_s).map(&:capitalize).join(" & "), } end def paths @paths ||= all_affected.map(&:root_paths).flatten end def all_affected_packages all_affected .map(&:packages) .map(&:keys) .flatten .uniq end def contains_ruby? all_affected_packages.include?(:bundler) end def contains_js? all_affected_packages.include?(:yarn) end end end