Remove thor from requirements. Start revamping Vagrant::CLI
This commit is contained in:
parent
5f567f30d8
commit
27491b554c
|
@ -33,8 +33,8 @@ begin
|
|||
logger.debug("Loading environment")
|
||||
env.load!
|
||||
|
||||
# Kick start the CLI
|
||||
Vagrant::CLI.start(ARGV, :env => env)
|
||||
# Execute the CLI interface
|
||||
env.cli(ARGV)
|
||||
rescue Vagrant::Errors::VagrantError => e
|
||||
logger.error("Vagrant experienced an error! Details:")
|
||||
logger.error(e.inspect)
|
||||
|
|
|
@ -23,6 +23,7 @@ module Vagrant
|
|||
autoload :Box, 'vagrant/box'
|
||||
autoload :BoxCollection, 'vagrant/box_collection'
|
||||
autoload :CLI, 'vagrant/cli'
|
||||
autoload :Command, 'vagrant/command'
|
||||
autoload :Config, 'vagrant/config'
|
||||
autoload :DataStore, 'vagrant/data_store'
|
||||
autoload :Downloaders, 'vagrant/downloaders'
|
||||
|
@ -122,6 +123,5 @@ Vagrant.config_keys.register(:linux) { Vagrant::Guest::Linux::LinuxConfig }
|
|||
Vagrant.config_keys.register(:solaris) { Vagrant::Guest::Solaris::SolarisConfig }
|
||||
|
||||
# Load the things which must be loaded before anything else.
|
||||
require 'vagrant/command'
|
||||
require 'vagrant/version'
|
||||
Vagrant::Plugin.load!
|
||||
|
|
|
@ -1,54 +1,16 @@
|
|||
require 'thor'
|
||||
|
||||
module Vagrant
|
||||
# Entrypoint for the Vagrant CLI. This class should never be
|
||||
# initialized directly (like a typical Thor class). Instead,
|
||||
# use {Environment#cli} to invoke the CLI.
|
||||
#
|
||||
# # Defining Custom CLI Commands
|
||||
#
|
||||
# If you're looking to define custom CLI commands, then look at
|
||||
# one of the two following classes:
|
||||
#
|
||||
# * {Command::Base} - Implementing a single command such as `vagrant up`, e.g.
|
||||
# one without subcommands. Also take a look at {Command::NamedBase}.
|
||||
# * {Command::GroupBase} - Implementing a command with subcommands, such as
|
||||
# `vagrant box`, which has the `list`, `add`, etc. subcommands.
|
||||
#
|
||||
# The above linked classes contain the main documentation for each
|
||||
# type of command.
|
||||
class CLI < Thor
|
||||
# Registers the given class with the CLI so it can be accessed.
|
||||
# The class must be a subclass of either {Command::Base} or {Command::GroupBase}.
|
||||
# Don't call this method directly, instead call the {Command::Base.register}
|
||||
# or {Command::GroupBase.register} methods.
|
||||
#
|
||||
# @param [Class] klass Command class
|
||||
# @param [String] name Command name, accessed at `vagrant NAME`
|
||||
# @param [String] usage Command usage, such as "vagrant NAME [--option]"
|
||||
# @param [String] description Description of the command shown during the
|
||||
# command listing.
|
||||
# @param [Hash] opts Other options (not gone into detail here, look at
|
||||
# the source instead).
|
||||
def self.register(klass, name, usage, description, opts=nil)
|
||||
opts ||= {}
|
||||
# Manages the command line interface to Vagrant.
|
||||
class CLI < Command::Base
|
||||
def initialize(argv, env)
|
||||
@env = env
|
||||
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
|
||||
end
|
||||
|
||||
if klass <= Command::GroupBase
|
||||
# A subclass of GroupBase is a subcommand, since it contains
|
||||
# many smaller commands within it.
|
||||
desc usage, description, opts
|
||||
subcommand name, klass
|
||||
elsif klass <= Command::Base
|
||||
# A subclass of Base is a single command, since it
|
||||
# is invoked as a whole (as Thor::Group)
|
||||
desc usage, description, opts
|
||||
define_method(name) { |*args| invoke klass, args }
|
||||
end
|
||||
|
||||
if opts[:alias]
|
||||
# Alises are defined for this command, so properly alias the
|
||||
# newly defined method/subcommand:
|
||||
map opts[:alias] => name
|
||||
def execute
|
||||
if @main_args.include?("-v") || @main_args.include?("--version")
|
||||
@env.ui.info(I18n.t("vagrant.commands.version.output",
|
||||
:version => Vagrant::VERSION),
|
||||
:prefix => false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,25 +1,5 @@
|
|||
module Vagrant
|
||||
module Command
|
||||
autoload :Base, 'vagrant/command/base'
|
||||
autoload :GroupBase, 'vagrant/command/group_base'
|
||||
autoload :Helpers, 'vagrant/command/helpers'
|
||||
autoload :NamedBase, 'vagrant/command/named_base'
|
||||
end
|
||||
end
|
||||
|
||||
# The built-in commands must always be loaded
|
||||
require 'vagrant/command/box'
|
||||
require 'vagrant/command/destroy'
|
||||
require 'vagrant/command/halt'
|
||||
require 'vagrant/command/init'
|
||||
require 'vagrant/command/package'
|
||||
require 'vagrant/command/provision'
|
||||
require 'vagrant/command/reload'
|
||||
require 'vagrant/command/resume'
|
||||
require 'vagrant/command/ssh'
|
||||
require 'vagrant/command/ssh_config'
|
||||
require 'vagrant/command/status'
|
||||
require 'vagrant/command/suspend'
|
||||
require 'vagrant/command/up'
|
||||
require 'vagrant/command/upgrade_to_060'
|
||||
require 'vagrant/command/version'
|
||||
|
|
|
@ -1,105 +1,48 @@
|
|||
require 'thor/group'
|
||||
require 'thor/actions'
|
||||
|
||||
module Vagrant
|
||||
module Command
|
||||
# A {Base} is the superclass for all commands which are single
|
||||
# commands, e.g. `vagrant init`, `vagrant up`. Not commands like
|
||||
# `vagrant box add`. For commands which have more subcommands, use
|
||||
# a {GroupBase}.
|
||||
#
|
||||
# A {Base} is a subclass of `Thor::Group`, so view the documentation
|
||||
# there on how to add arguments, descriptions etc. The important note
|
||||
# about this is that when invoked, _all public methods_ will be called
|
||||
# in the order they are defined. If you don't want a method called when
|
||||
# the command is invoked, it must be made `protected` or `private`.
|
||||
#
|
||||
# The best way to get examples of how to create your own command is to
|
||||
# view the various Vagrant commands, which are relatively simple, and
|
||||
# can be found in the Vagrant source tree at `lib/vagrant/command/`.
|
||||
#
|
||||
# # Defining a New Command
|
||||
#
|
||||
# To define a new single command, create a new class which inherits
|
||||
# from this class, then call {register} to register the command. That's
|
||||
# it! When the command is invoked, _all public methods_ will be called.
|
||||
# Below is an example `SayHello` class:
|
||||
#
|
||||
# class SayHello < Vagrant::Command::Base
|
||||
# register "hello", "Says hello"
|
||||
#
|
||||
# def hello
|
||||
# env.ui.info "Hello"
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# In this case, the above class is invokable via `vagrant hello`. To give
|
||||
# this a try, just copy and paste the above into a Vagrantfile somewhere.
|
||||
# The command will be available for that project!
|
||||
#
|
||||
# Also note that the above example uses `env.ui` to output. It is recommended
|
||||
# you use this instead of raw "puts" since it is configurable and provides
|
||||
# additional functionality, such as colors and asking for user input. See
|
||||
# the {UI} class for more information.
|
||||
#
|
||||
# ## Defining Command-line Options
|
||||
#
|
||||
# Most command line actions won't be as simple as `vagrant hello`, and will
|
||||
# probably require parameters or switches. Luckily, Thor makes adding these
|
||||
# easy:
|
||||
#
|
||||
# class SayHello < Vagrant::Command::Base
|
||||
# register "hello", "Says hello"
|
||||
# argument :name, :type => :string
|
||||
#
|
||||
# def hello
|
||||
# env.ui.info "Hello, #{name}"
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Then, the above can be invoked with `vagrant hello Mitchell` which would
|
||||
# output "Hello, Mitchell." If instead you're looking for switches, such as
|
||||
# "--name Mitchell", then take a look at `class_option`, an example of which
|
||||
# can be found in the {PackageCommand}.
|
||||
class Base < Thor::Group
|
||||
include Thor::Actions
|
||||
include Helpers
|
||||
|
||||
attr_reader :env
|
||||
|
||||
# Register the command with the main Vagrant CLI under the
|
||||
# given name. The name will be used for accessing it from the CLI,
|
||||
# so if you name it "lamp", then the command to invoke this
|
||||
# will be `vagrant lamp`.
|
||||
#
|
||||
# The description is used when the help is listed, and is meant to be
|
||||
# a brief (one sentence) description of what the command does.
|
||||
#
|
||||
# Some additional options may be passed in as the last parameter:
|
||||
#
|
||||
# * `:alias` - If given as an array or string, these will be aliases
|
||||
# for the same command. For example, `vagrant version` is also
|
||||
# `vagrant --version` and `vagrant -v`
|
||||
#
|
||||
# @param [String] usage
|
||||
# @param [String] description
|
||||
# @param [Hash] opts
|
||||
def self.register(usage, description, opts=nil)
|
||||
desc description
|
||||
CLI.register(self, extract_name_from_usage(usage), usage, desc, opts)
|
||||
end
|
||||
|
||||
def initialize(*args)
|
||||
super
|
||||
initialize_environment(*args)
|
||||
end
|
||||
|
||||
class Base
|
||||
protected
|
||||
|
||||
# Extracts the name of the command from a usage string. Example:
|
||||
# `init [box_name] [box_url]` becomes just `init`.
|
||||
def self.extract_name_from_usage(usage)
|
||||
/^([-_a-zA-Z0-9]+)(\s+(.+?))?$/.match(usage).to_a[1]
|
||||
# This method will split the argv given into three parts: the
|
||||
# flags to this command, the subcommand, and the flags to the
|
||||
# subcommand. For example:
|
||||
#
|
||||
# -v status -h -v
|
||||
#
|
||||
# The above would yield 3 parts:
|
||||
#
|
||||
# ["-v"]
|
||||
# "status"
|
||||
# ["-h", "-v"]
|
||||
#
|
||||
# These parts are useful because the first is a list of arguments
|
||||
# given to the current command, the second is a subcommand, and the
|
||||
# third are the commands given to the subcommand.
|
||||
#
|
||||
# @return [Array] The three parts.
|
||||
def split_main_and_subcommand(argv)
|
||||
# Initialize return variables
|
||||
main_args = nil
|
||||
sub_command = nil
|
||||
sub_args = []
|
||||
|
||||
# We split the arguments into two: One set containing any
|
||||
# flags before a word, and then the rest. The rest are what
|
||||
# get actually sent on to the subcommand.
|
||||
argv.each_index do |i|
|
||||
if !argv[i].start_with?("-")
|
||||
# We found the beginning of the sub command. Split the
|
||||
# args up.
|
||||
main_args = argv[0, i]
|
||||
sub_command = argv[i]
|
||||
sub_args = argv[i + 1, argv.length - i + 1]
|
||||
end
|
||||
end
|
||||
|
||||
# Handle the case that argv was empty or didn't contain any subcommand
|
||||
main_args = argv.dup if main_args.nil?
|
||||
|
||||
return [main_args, sub_command, sub_args]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -172,7 +172,7 @@ module Vagrant
|
|||
# env.cli("package", "--vagrantfile", "Vagrantfile")
|
||||
#
|
||||
def cli(*args)
|
||||
CLI.start(args.flatten, :env => self)
|
||||
CLI.new(args.flatten, self).execute
|
||||
end
|
||||
|
||||
# Returns the host object associated with this environment.
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
require File.expand_path("../../../base", __FILE__)
|
||||
|
||||
describe Vagrant::Command::Base do
|
||||
describe "splitting the main and subcommand args" do
|
||||
let(:instance) do
|
||||
Class.new(described_class) do
|
||||
# Make the method public since it is normal protected
|
||||
public :split_main_and_subcommand
|
||||
end.new
|
||||
end
|
||||
|
||||
it "should work when given all 3 parts" do
|
||||
result = instance.split_main_and_subcommand(["-v", "status", "-h", "-v"])
|
||||
result.should == [["-v"], "status", ["-h", "-v"]]
|
||||
end
|
||||
|
||||
it "should work when given only a subcommand and args" do
|
||||
result = instance.split_main_and_subcommand(["status", "-h"])
|
||||
result.should == [[], "status", ["-h"]]
|
||||
end
|
||||
|
||||
it "should work when given only main flags" do
|
||||
result = instance.split_main_and_subcommand(["-v", "-h"])
|
||||
result.should == [["-v", "-h"], nil, []]
|
||||
end
|
||||
|
||||
it "should work when given only a subcommand" do
|
||||
result = instance.split_main_and_subcommand(["status"])
|
||||
result.should == [[], "status", []]
|
||||
end
|
||||
end
|
||||
end
|
|
@ -21,7 +21,6 @@ Gem::Specification.new do |s|
|
|||
s.add_dependency "net-ssh", "~> 2.1.4"
|
||||
s.add_dependency "net-scp", "~> 1.0.4"
|
||||
s.add_dependency "i18n", "~> 0.6.0"
|
||||
s.add_dependency "thor", "~> 0.14.6"
|
||||
s.add_dependency "virtualbox", "~> 0.9.1"
|
||||
|
||||
s.add_development_dependency "rake"
|
||||
|
|
Loading…
Reference in New Issue