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
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)
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