class ChefCLI::Command::Diff

def apply_params!(params)

def apply_params!(params)
  remaining_args = parse_options(params)
  if no_comparison_specified?(remaining_args)
    ui.err("No comparison specified")
    ui.err("")
    ui.err(opt_parser)
    false
  elsif conflicting_args_and_opts_given?(remaining_args)
    ui.err("Conflicting arguments and options: git and Policy Group comparisons cannot be mixed")
    ui.err("")
    ui.err(opt_parser)
    false
  elsif conflicting_git_options_given?
    ui.err("Conflicting git options: --head and --git are exclusive")
    ui.err("")
    ui.err(opt_parser)
    false
  elsif config[:head]
    set_policyfile_path_from_args(remaining_args)
    @old_base = Policyfile::ComparisonBase::Git.new("HEAD", policyfile_lock_relpath)
    @new_base = Policyfile::ComparisonBase::Local.new(policyfile_lock_relpath)
    true
  elsif config[:git]
    set_policyfile_path_from_args(remaining_args)
    parse_git_comparison(config[:git])
  else
    set_policyfile_path_from_args(remaining_args)
    parse_server_comparison(remaining_args)
  end
end

def comparing_policy_groups?

def comparing_policy_groups?
  !(config[:git] || config[:head])
end

def conflicting_args_and_opts_given?(args)

def conflicting_args_and_opts_given?(args)
  (config[:git] || config[:head]) && policy_group_comparison?(args)
end

def conflicting_git_options_given?

def conflicting_git_options_given?
  config[:git] && config[:head]
end

def debug?

def debug?
  !!config[:debug]
end

def differ(ui = self.ui)

def differ(ui = self.ui)
  Policyfile::Differ.new(old_name: old_base.name,
                         old_lock:,
                         new_name: new_base.name,
                         new_lock:,
                         ui:)
end

def handle_error(error)

def handle_error(error)
  ui.err("Error: #{error.message}")
  if error.respond_to?(:reason)
    ui.err("Reason: #{error.reason}")
    ui.err("")
    ui.err(error.extended_error_info) if debug?
    ui.err(error.cause.backtrace.join("\n")) if debug?
  end
end

def http_client

def http_client
  @http_client ||= Chef::ServerAPI.new(chef_config.chef_server_url,
    signing_key_filename: chef_config.client_key,
    client_name: chef_config.node_name)
end

def initialize(*args)

def initialize(*args)
  super
  @ui = UI.new
  @old_base = nil
  @new_base = nil
  @policyfile_relative_path = nil
  @storage_config = nil
  @http_client = nil
  @old_lock = nil
  @new_lock = nil
end

def local_lock

def local_lock
  @local_lock ||= local_lock_comparison_base.lock
end

def local_lock_comparison_base

particular policy_group.
policy_name which is needed to query the server for the lockfile of a
ComparisonBase for the local lockfile. This is used to get the
def local_lock_comparison_base
  Policyfile::ComparisonBase::Local.new(policyfile_lock_relpath)
end

def materialize_locks

def materialize_locks
  @old_lock = old_base.lock
  @new_lock = new_base.lock
end

def new_lock

def new_lock
  materialize_locks unless @new_lock
  @new_lock
end

def no_comparison_specified?(args)

def no_comparison_specified?(args)
  !policy_group_comparison?(args) && !config[:head] && !config[:git]
end

def old_lock

def old_lock
  materialize_locks unless @old_lock
  @old_lock
end

def parse_git_comparison(git_ref)

def parse_git_comparison(git_ref)
  if git_ref.include?("...")
    old_ref, new_ref, *extra = git_ref.split("...")
    @old_base, @new_base = [old_ref, new_ref].map do |r|
      Policyfile::ComparisonBase::Git.new(r, policyfile_lock_relpath)
    end
    unless extra.empty?
      ui.err("Unable to parse git comparison `#{git_ref}`. Only 2 references can be specified.")
      return false
    end
  else
    @old_base = Policyfile::ComparisonBase::Git.new(git_ref, policyfile_lock_relpath)
    @new_base = Policyfile::ComparisonBase::Local.new(policyfile_lock_relpath)
  end
  true
end

def parse_server_comparison(args)

def parse_server_comparison(args)
  comparison_string = args.last
  if comparison_string.include?("...")
    old_pgroup, new_pgroup, *extra = comparison_string.split("...")
    @old_base, @new_base = [old_pgroup, new_pgroup].map do |g|
      Policyfile::ComparisonBase::PolicyGroup.new(g, policy_name, http_client)
    end
    unless extra.empty?
      ui.err("Unable to parse policy group comparison `#{comparison_string}`. Only 2 references can be specified.")
      return false
    end
  else
    @old_base = Policyfile::ComparisonBase::PolicyGroup.new(comparison_string, policy_name, http_client)
    @new_base = Policyfile::ComparisonBase::Local.new(policyfile_lock_relpath)
  end
  true
end

def policy_group_comparison?(args)

push`, etc.).
that would be inconsistent with other commands (`chef install`, `chef
ruby policyfile. It would be easier if we used an option like `-f`, but
necessary because we support an optional argument with the path to the
Try to detect if the only argument given is a policyfile path. This is
def policy_group_comparison?(args)
  return false if args.empty?
  return true if args.size > 1
  !(args.first =~ /\.rb\Z/)
end

def policy_name

def policy_name
  local_lock["name"]
end

def policyfile_lock_relpath

def policyfile_lock_relpath
  storage_config.policyfile_lock_filename
end

def print_diff

def print_diff
  # eagerly evaluate locks so we hit any errors before we've entered
  # pagerland. Also, git commands behave weirdly when run while the pager
  # is active, doing this eagerly also avoids that issue
  materialize_locks
  Pager.new(enable_pager: config[:pager]).with_pager do |pager|
    differ = differ(pager.ui)
    differ.run_report
  end
end

def run(params = [])

def run(params = [])
  return 1 unless apply_params!(params)
  print_diff
  0
rescue PolicyfileServiceError => e
  handle_error(e)
  1
end

def set_policyfile_path_from_args(args)

def set_policyfile_path_from_args(args)
  policyfile_relative_path =
    if !comparing_policy_groups?
      args.first || "Policyfile.rb"
    elsif args.size == 1
      "Policyfile.rb"
    else
      args.first
    end
  @storage_config = Policyfile::StorageConfig.new.use_policyfile(policyfile_relative_path)
end