lib/chef-cli/command/shell_init.rb
# # Copyright:: Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # autoload :ERB, "erb" require_relative "../commands_map" require_relative "../builtin_commands" require_relative "base" require_relative "../dist" module Mixlib autoload :ShellOut, "mixlib/shellout" end module ChefCLI class ShellCompletionTemplateContext def commands ChefCLI.commands_map.command_specs.inject({}) do |cmd_info, (_key, cmd_spec)| cmd_info[cmd_spec.name] = cmd_spec.description cmd_info end end def get_binding binding end end module Command class ShellInit < ChefCLI::Command::Base SUPPORTED_SHELLS = %w{ bash fish zsh sh powershell posh}.map(&:freeze).freeze banner(<<~HELP) Usage: #{ChefCLI::Dist::EXEC} shell-init `#{ChefCLI::Dist::EXEC} shell-init` modifies your shell environment to make ChefCLI or Workstation your default Ruby. To enable for just the current shell session: In sh, bash, and zsh: eval "$(#{ChefCLI::Dist::EXEC} shell-init SHELL_NAME)" In fish: eval (#{ChefCLI::Dist::EXEC} shell-init fish) In Powershell: #{ChefCLI::Dist::EXEC} shell-init powershell | Invoke-Expression To permanently enable: In sh, bash, and zsh: echo 'eval "$(#{ChefCLI::Dist::EXEC} shell-init SHELL_NAME)"' >> ~/.YOUR_SHELL_RC_FILE In fish: echo 'eval (#{ChefCLI::Dist::EXEC} shell-init SHELL_NAME)' >> ~/.config/fish/config.fish In Powershell "#{ChefCLI::Dist::EXEC} shell-init powershell | Invoke-Expression" >> $PROFILE OPTIONS: HELP option :omnibus_dir, long: "--omnibus-dir OMNIBUS_DIR", description: "Alternate path to omnibus install (used for testing)" def initialize super @shell_completion_template_context = nil end def omnibus_root config[:omnibus_dir] || super end def run(argv) # Currently we don't have any shell-specific features, so we ignore the # shell name. We'll need it if we add completion. remaining_args = parse_options(argv) shell_name = remaining_args.first if shell_name.nil? err("Please specify what shell you are using\n") err(opt_parser.to_s) return 1 elsif !SUPPORTED_SHELLS.include?(shell_name) err("Shell `#{shell_name}' is not currently supported") err("Supported shells are: #{SUPPORTED_SHELLS.join(" ")}") return 1 end env = omnibus_env.dup path = env.delete("PATH") export(shell_name, "PATH", path) env.each do |var_name, value| export(shell_name, var_name, value) end emit_shell_cmd(completion_for(shell_name)) 0 end def emit_shell_cmd(cmd) msg(cmd) unless cmd.empty? end def completion_for(shell) return "" unless (completion_template_basename = completion_template_for(shell)) completion_template_path = expand_completion_template_path(completion_template_basename) erb = ERB.new(File.read(completion_template_path), trim_mode: "-") context_binding = shell_completion_template_context.get_binding erb.result(context_binding) end def completion_template_for(shell) case shell when "bash" "bash.sh.erb" when "fish" "chef.fish.erb" when "zsh" "zsh.zsh.erb" else # Pull requests accepted! nil end end def expand_completion_template_path(basename) File.join(File.expand_path("../completions", __dir__), basename) end def shell_completion_template_context @shell_completion_template_context ||= ShellCompletionTemplateContext.new end def export(shell, var, val) case shell when "sh", "bash", "zsh" posix_shell_export(var, val) when "fish" fish_shell_export(var, val) when "powershell", "posh" powershell_export(var, val) end end def posix_shell_export(var, val) emit_shell_cmd(%Q{export #{var}="#{val}"}) end def fish_shell_export(var, val) # Fish's syntax for setting PATH is special. Path elements are # divided by spaces (instead of colons). We also send STDERR to # /dev/null to avoid Fish's helpful warnings about nonexistent # PATH elements. if var == "PATH" emit_shell_cmd(%Q{set -gx #{var} "#{val.split(":").join('" "')}" 2>/dev/null;}) else emit_shell_cmd(%Q{set -gx #{var} "#{val}";}) end end def powershell_export(var, val) emit_shell_cmd(%Q{$env:#{var}="#{val}"}) end def check_license_acceptance # It gives a very weird error if users try to eval the shell-init command and it requests the # license from them. Instead, let users shell-init without accepting the license. end end end end