module PDK::CLI @console_cmd = @base_cmd.define_command do name 'console' usage _('console [console_options]') summary _('(Experimental) Start a session of the puppet debugger console.') default_subcommand 'help' description _(<<-EOF The pdk console runs a interactive session of the puppet debugger tool to test out snippets of code, run language evaluations, datatype prototyping and much more. A virtual playground for your puppet code! For usage details see the puppet debugger docs at https://docs.puppet-debugger.com. EOF ) PDK::CLI.puppet_version_options(self) PDK::CLI.puppet_dev_option(self) # we have to skip option parsing because it is expected the user # will be passing additional args that are passed to the debugger skip_option_parsing # TODO: using -h or --help skips the pdk help and passes to puppet debugger help run do |_opts, args, _cmd| require 'pdk/cli/util' require 'pdk/util' PDK::CLI::Util.ensure_in_module!( message: _('Console can only be run from inside a valid module directory'), log_level: :fatal, ) PDK::CLI::Util.module_version_check processed_options, processed_args = process_opts(args) PDK::CLI::Util.validate_puppet_version_opts(processed_options) PDK::CLI::Util.analytics_screen_view('console', args) # TODO: figure out if we need to remove default configs set by puppet # so it is scoped for the module only # "--environmentpath"... flags = if PDK::Util.in_module_root? ["--basemodulepath=#{base_module_path}", "--modulepath=#{base_module_path}"] else [] end debugger_args = ['debugger'] + processed_args + flags result = run_in_module(processed_options, debugger_args) exit result[:exit_code] end # Logs a fatal message about the gem missing and how to add it def inform_user_for_missing_gem(gem_name = 'puppet-debugger', version = '~> 0.14') PDK.logger.fatal(<<-EOF Your Gemfile is missing the #{gem_name} gem. You can add the missing gem by updating your #{File.join(PDK::Util.module_root, '.sync.yml')} file with the following and running pdk update. Gemfile: required: ":development": - gem: #{gem_name} version: \"#{version}\" EOF ) end # @return [Boolean] - true if the gem was found in the lockfile # @param [String] - name of ruby gem to check in bundle lockfile def gem_in_bundle_lockfile?(gem_name) require 'bundler' require 'pdk/util/bundler' lock_file_path = PDK::Util::Bundler::BundleHelper.new.gemfile_lock PDK.logger.debug("Checking lockfile #{lock_file_path} for #{gem_name}") lock_file = ::Bundler::LockfileParser.new(::Bundler.read_file(lock_file_path)) !lock_file.specs.find { |spec| spec.name.eql?(gem_name) }.nil? rescue ::Bundler::GemfileNotFound => e PDK.logger.debug _(e.message) false end def check_fixtures_dir existing_path = base_module_path.split(':').find do |path| PDK::Util::Filesystem.directory?(path) && Dir.entries(path).length > 2 end PDK.logger.warn _('Module fixtures not found, please run pdk bundle exec rake spec_prep.') unless existing_path end # @return [Array] - array of split options [{:"puppet-version"=>"6.9.0"}, ['--loglevel=debug']] # options are for the pdk and debugger pass through def process_opts(opts) args = opts.map do |e| if e =~ %r{\A-{2}puppet|pe\-version|dev} value = e.split('=') (value.count < 2) ? value + [''] : value end end args = args.compact.to_h # symbolize keys args = args.inject({}) do |memo, (k, v)| # rubocop:disable Style/EachWithObject memo[k.sub('--', '').to_sym] = v memo end # pass through all other args that are bound for puppet debugger processed_args = opts.map { |e| e unless e =~ %r{\A-{2}puppet|pe\-version|dev} }.compact [args, processed_args] end # @param opts [Hash] - the options passed into the CRI command # @param bundle_args [Array] array of bundle exec args and puppet debugger args # @return [Hash] - a command result hash def run_in_module(opts, bundle_args) require 'pdk/cli/exec' require 'pdk/cli/exec/interactive_command' require 'pdk/util/ruby_version' require 'pdk/util/bundler' check_fixtures_dir output = opts[:debug].nil? puppet_env = PDK::CLI::Util.puppet_from_opts_or_env(opts, output) gemfile_env = PDK::Util::Bundler::BundleHelper.gemfile_env(puppet_env[:gemset]) PDK::Util::RubyVersion.use(puppet_env[:ruby_version]) PDK::Util::RubyVersion.instance(puppet_env[:ruby_version]) PDK::Util::Bundler.ensure_bundle!(puppet_env[:gemset]) unless gem_in_bundle_lockfile?('puppet-debugger') inform_user_for_missing_gem return { exit_code: 1 } end debugger_args = %w[exec puppet] + bundle_args command = PDK::CLI::Exec::InteractiveCommand.new(PDK::CLI::Exec.bundle_bin, *debugger_args).tap do |c| c.context = :pwd c.update_environment(gemfile_env) end command.execute! end # @return [String] - the basemodulepath of the fixtures and modules from the current module # also includes ./modules in case librarian puppet is used def base_module_path base_module_path = File.join(PDK::Util.module_fixtures_dir, 'modules') "#{base_module_path}:#{File.join(PDK::Util.module_root, 'modules')}" end end end