Redo the CLI using <subject> <verb> instead of the other way around
7 files changed, 222 insertions(+), 156 deletions(-)

M README.md
M lib/assemblage/cli.rb
M lib/assemblage/command/start.rb => lib/assemblage/command/repo.rb
M lib/assemblage/command/create.rb => lib/assemblage/command/server.rb
M lib/assemblage/command/add.rb => lib/assemblage/command/worker.rb
M lib/assemblage/repository.rb
M lib/assemblage/server.rb
M README.md +10 -10
@@ 63,19 63,19 @@ course, run all of this on a single host
 You'll first need a server to manage your assemblies:
 
     example $ sudo gem install assemblage
-    example $ assemblage create server /usr/local/assemblage
+    example $ assemblage server create /usr/local/assemblage
     Creating a server run directory in /usr/local/assemblage...
     Generating a server key...
     Creating the assemblies database...
     done.
 
     You can start the assembly server like so:
-      assemblage start server /usr/local/assemblage
+      assemblage server start /usr/local/assemblage
     
     Server public key is:
       &}T0.[{MZSJC]roN-{]x2QCkG+dXki!6j!.1JU1u
 
-    example $ assemblage start server /usr/local/assemblage
+    example $ assemblage server start /usr/local/assemblage
     Starting assembly server at:
       tcp://example.com:7872
 

          
@@ 89,7 89,7 @@ can make it as simple or complex as you 
     user@example-client $ sudo gem install assemblage
     user@example-client $ mkdir -p /usr/local/assemblage
     user@example-client $ cd /usr/local/assemblage
-    user@example-client $ assemblage create worker \
+    user@example-client $ assemblage worker create \
       -t freebsd,freebsd11,ruby,ruby24,ruby25,python,python27,zeromq,libpq worker1
     Creating a new assembly worker run directory in
       /usr/local/assemblage/worker1...

          
@@ 100,7 100,7 @@ can make it as simple or complex as you 
 
 Now we need to register the worker with the server. On the server host:
 
-    user@example $ assemblage add worker example-client-worker1 \
+    user@example $ assemblage worker add example-client-worker1 \
       "PJL=qK@SHy3#re-w@W)4C]Aj+aD}toGf*Y*SOOZ4"
     Approving connections from example-client-worker1...
     done.

          
@@ 109,7 109,7 @@ Now go back to the worker and tell it th
 just set up:
 
     user@example-client $ cd /usr/local/assemblage/worker1
-    user@example-client $ assemblage add server \
+    user@example-client $ assemblage server add \
       tcp://example.com:7872 "&}T0.[{MZSJC]roN-{]x2QCkG+dXki!6j!.1JU1u"
     Talking to tcp://example.com:7872...
     Testing registration... success.

          
@@ 118,7 118,7 @@ just set up:
 Now you can start the worker, which will listen for jobs it can work on.
 
     user@example-client $ cd /usr/local/assemblage/worker1
-    user@example-client $ assemblage start worker
+    user@example-client $ assemblage worker start
     Starting assembly worker `worker1`...
     Connecting to assembly servers...
        example... done.

          
@@ 133,8 133,8 @@ operations:
     user@example-repo $ sudo gem install assemblage hglib
     user@example-repo $ mkdir /usr/local/hg/repos/.assemblage
     user@example-repo $ cd /usr/local/hg/repos/.assemblage
-    user@example-repo $ assemblage create repo project1
-    Creating a new assemblage repo run directory in
+    user@example-repo $ assemblage repo setup project1
+    Setting up a new assemblage repo config directory in
       /usr/local/hg/repos/.assemblage...
     Set up with repo name: project1
     Client public key is:

          
@@ 144,7 144,7 @@ operations:
 Now we'll need to register the repo on the server like we did before for the
 worker:
 
-    user@example $ assemblage add repo --type=hg project1 \
+    user@example $ assemblage repo add --type=hg project1 \
         'bq9VheQbLtcu]LGK4I&xzK3^UW0Iyak/6<YS=^$w' \
         http://repo.example.com/project1
     Looking for repo registration... found.

          
M lib/assemblage/cli.rb +1 -1
@@ 155,7 155,7 @@ module Assemblage::CLI
 	### Return the TTY prompt used by the command to communicate with the
 	### user.
 	def self::prompt
-		@prompt ||= TTY::Prompt.new
+		@prompt ||= TTY::Prompt.new( output: $stderr )
 	end
 
 

          
M lib/assemblage/command/start.rb => lib/assemblage/command/repo.rb +79 -20
@@ 5,39 5,98 @@ require 'pathname'
 require 'assemblage/cli' unless defined?( Assemblage::CLI )
 
 
-# Command to start a Assemblage server
-module Assemblage::CLI::StartServer
+# Assemblage Repository commands
+module Assemblage::CLI::RepoCommand
 	extend Assemblage::CLI::Subcommand
 
 
-	desc "Start an Assemblage daemon"
-	command :start do |start|
+	SETUP_ADVICE = %{
+		Now you can tell a server to accept commits from a repository on this host
+		like so:
+		  assemblage repo add %{name} '%{public_key}' <uri>
+
+		Once it is registered, you can add :
+		  assemblage commit %{directory}
+
+	}.gsub( /^\t+/, '' )
+
+
+	desc "Repository commands"
+	command :repo do |repo|
+
+		repo.desc "Specify a name that will identify the repository server"
+		repo.flag [:N, :name], type: String,
+			must_match: Assemblage::Auth::CLIENT_NAME_PATTERN,
+			required: true
 
-		start.desc 'Start an assembly server'
-		start.long_desc <<-END_DESC
-		Start the Assemblage server in the specified DIRECTORY. If not specified, the
-		DIRECTORY will default to the current working directory.
+		repo.desc 'Set up a new run directory for repository hooks'
+		repo.long_desc <<-END_DESC
+		Set up a new run directory for hooks that run in one or more repositories.
+		This is how the commit (or other) hook authenticates to the server when
+		it runs.
 		END_DESC
-		start.arg :DIRECTORY, :optional
-		start.command :server do |server|
-			server.action do |globals, options, args|
-				Assemblage::Server.run( args.shift )
+		repo.arg :DIRECTORY
+		repo.command :setup do |setup|
+
+			setup.action do |globals, options, args|
+				directory = Pathname( args.shift || '.' ).expand_path
+				name = options.name
+
+				prompt.say "Creating a repo run directory in %s..." % [ directory ]
+				Assemblage::Repository.setup_run_directory( directory )
+
+				prompt.say "Generating a repo key..."
+				Assemblage::Repository.generate_cert
+
+				msg = SETUP_ADVICE % {
+					public_key: Assemblage::Repository.public_key,
+					directory: directory,
+					name: name
+				}
+				prompt.say( msg )
 			end
 		end
 
 
-		start.desc 'Start an assembly worker'
-		start.long_desc <<-END_DESC
-		Start an Assemblage worker in the specified DIRECTORY. If not specified, the
-		DIRECTORY will default to the current working directory.
+		repo.desc "Output the repo's public key"
+		repo.long_desc <<-END_DESC
+		Output the repo's public key to STDOUT.
 		END_DESC
-		start.arg :DIRECTORY, :optional
-		start.command :worker do |server|
-			server.action do |globals, options, args|
-				Assemblage::Worker.run( args.shift )
+		repo.arg :DIRECTORY, :optional
+		repo.command 'show-key' do |show_key|
+			show_key.action do |globals, options, args|
+				directory = Pathname( args.shift || '.' ).expand_path
+
+				Assemblage.use_run_directory( directory )
+				prompt.say "Worker's public key:"
+				puts Assemblage::Repository.public_key
 			end
 		end
 
+
+		repo.desc "Add a new repository key to a server"
+		repo.long_desc <<-END_DESC
+		Allow connections from a repository named NAME to the server config
+		in SERVER_DIRECTORY.
+		END_DESC
+		repo.arg :NAME
+		repo.arg :PUBLIC_KEY
+		repo.arg :SERVER_DIRECTORY, :optional
+		repo.command :add do |add|
+
+			add.action do |globals, options, args|
+				name = args.shift or help_now!( "Missing the repo name." )
+				public_key = args.shift or help_now!( "Missing the repo public key." )
+
+				Assemblage.use_run_directory( args.shift )
+
+				prompt.say "Approving connections from %s..." % [ name ]
+				Assemblage::Server.add_repo( name, public_key )
+				prompt.say "done."
+			end
+
+		end
+
 	end
 
 end # module Assemblage::CLI::StartServer

          
M lib/assemblage/command/create.rb => lib/assemblage/command/server.rb +34 -96
@@ 4,11 4,11 @@ 
 require 'assemblage/cli' unless defined?( Assemblage::CLI )
 
 
-# Command to create a new Assembly server
-module Assemblage::CLI::CreateServer
+# Assemblage server commands
+module Assemblage::CLI::ServerCommand
 	extend Assemblage::CLI::Subcommand
 
-	SERVER_ADVICE = %{
+	CREATE_ADVICE = %{
 		You can start the assembly server like so:
 		  assemblage start server %{directory}
 

          
@@ 18,43 18,23 @@ module Assemblage::CLI::CreateServer
 	}.gsub( /^\t+/, '' )
 
 
-	WORKER_ADVICE = %{
-		Now you can register this worker with a server like so:
-		  assemblage add worker %{name} '%{public_key}' <server directory>
 
-		Once it is registered, you can start the assembly worker like so:
-		  assemblage start worker %{directory}
-
-	}.gsub( /^\t+/, '' )
-
+	desc "Server commands"
+	command :server do |server|
 
-	REPO_ADVICE = %{
-		Now you can tell a server to accept commits from a repository on this host
-		like so:
-		  assemblage add repo %{name} '%{public_key}' <uri>
-
-		Once it is registered, you can add :
-		  assemblage commit %{directory}
+		server.desc "Specify the URI to a database to use."
+		server.flag [:D, :database], type: String
 
-	}.gsub( /^\t+/, '' )
-
-
-	desc "Create a run directory for an Assemblage server or worker"
-	command :create do |create|
-
-		create.desc 'Set up a new assembly server'
-		create.long_desc <<-END_DESC
+		server.desc 'Set up a new assembly server'
+		server.long_desc <<-END_DESC
 		Set up a new assembly server in the given DIRECTORY. If the DIRECTORY is
 		not specified, the current directory will be used. If the target directory
 		is not empty, this command will abort.
 		END_DESC
-		create.arg :DIRECTORY
-		create.command :server do |server|
+		server.arg :DIRECTORY
+		server.command :create do |create|
 
-			server.desc "Specify the URI to a database to use."
-			server.flag [:D, :database], type: String
-
-			server.action do |globals, options, args|
+			create.action do |globals, options, args|
 				directory = Pathname( args.shift || '.' ).expand_path
 
 				if options[:database]

          
@@ 71,7 51,7 @@ module Assemblage::CLI::CreateServer
 				prompt.say "Creating the assemblies database..."
 				Assemblage::Server.create_database
 
-				msg = SERVER_ADVICE % {
+				msg = CREATE_ADVICE % {
 					public_key: Assemblage::Server.public_key,
 					directory: directory
 				}

          
@@ 80,81 60,39 @@ module Assemblage::CLI::CreateServer
 		end
 
 
-		create.desc 'Set up a new assembly worker'
-		create.long_desc <<-END_DESC
-		Set up a new assembly worker in the given DIRECTORY. If the DIRECTORY is
-		not specified, the current directory will be used. If the target directory
-		is not empty, this command will abort.
+		server.desc "Add a new server to a worker"
+		server.long_desc <<-END_DESC
+		Add a cert and configuration for a new server to a worker run directory.
 		END_DESC
-		create.arg :DIRECTORY
-		create.command :worker do |worker|
-
-			worker.desc "Specify a name that will identify the worker on any " +
-				"servers it registers with"
-			worker.flag [:N, :name], type: String,
-				must_match: Assemblage::Auth::CLIENT_NAME_PATTERN
-
-			worker.desc "Specify one or more tags that indicate what assemblies " +
-				"the worker should accept"
-			worker.flag [:t, :tags], type: Array
+		server.arg :URL
+		server.arg :PUBLIC_KEY
+		server.arg :WORKER_DIRECTORY, :optional
+		server.command :add do |add|
 
-			worker.action do |globals, options, args|
-				directory = Pathname( args.shift || '.' ).expand_path
-
-				name = options.name || "%s-%s" %
-					[ Socket.gethostname.downcase.gsub('.', '-'), directory.basename ]
-				tags = options.tags
-
-				prompt.say "Creating a worker run directory in %s..." % [ directory ]
-				Assemblage::Worker.setup_run_directory( directory, name, tags )
+			add.action do |globals, options, args|
+				url = args.shift or help_now!( "Missing the server url." )
+				public_key = args.shift or help_now!( "Missing the server public key." )
 
-				prompt.say "Generating a worker key..."
-				Assemblage::Worker.generate_cert
+				Assemblage.use_run_directory( args.shift )
+				Assemblage::Worker.add_server( url, public_key )
 
-				msg = WORKER_ADVICE % {
-					name: name,
-					public_key: Assemblage::Worker.public_key,
-					directory: directory
-				}
-				prompt.say( msg )
+				prompt.say "done."
 			end
 		end
 
 
-		create.desc 'Set up a new run directory for repository hooks'
-		create.long_desc <<-END_DESC
-		Set up a new run directory for hooks that run in one or more repositories.
-		This is how the commit (or other) hook authenticates to the server when
-		it runs.
+		server.desc 'Start an assembly server'
+		server.long_desc <<-END_DESC
+		Start the Assemblage server in the specified DIRECTORY. If not specified, the
+		DIRECTORY will default to the current working directory.
 		END_DESC
-		create.arg :DIRECTORY
-		create.command :repo do |repo|
-
-			repo.desc "Specify a name that will identify the repository server"
-			repo.flag [:R, :repo_name], type: String,
-				must_match: Assemblage::Auth::CLIENT_NAME_PATTERN,
-				required: true
-
-			repo.action do |globals, options, args|
-				directory = Pathname( args.shift || '.' ).expand_path
-				name = options.name
-
-				prompt.say "Creating a repo run directory in %s..." % [ directory ]
-				Assemblage::Repository.setup_run_directory( name, directory )
-
-				prompt.say "Generating a repo key..."
-				Assemblage::Repository.generate_cert
-
-				msg = REPO_ADVICE % {
-					public_key: Assemblage::Repository.public_key,
-					directory: directory
-				}
-				prompt.say( msg )
+		server.arg :DIRECTORY, :optional
+		server.command :start do |start|
+			start.action do |globals, options, args|
+				Assemblage::Server.run( args.shift )
 			end
 		end
 
-
-
 	end
 
 end # module Assemblage::CLI::CreateServer

          
M lib/assemblage/command/add.rb => lib/assemblage/command/worker.rb +87 -27
@@ 5,24 5,91 @@ require 'socket'
 require 'assemblage/cli' unless defined?( Assemblage::CLI )
 require 'assemblage/db_object'
 
-# Command to add an Aeembly server to a worker's config
-module Assemblage::CLI::AddServer
+# Aseemblage worker commands
+module Assemblage::CLI::WorkerCommand
 	extend Assemblage::CLI::Subcommand
 
+	CREATE_ADVICE = %{
+		Now you can register this worker with a server like so:
+		  assemblage add worker %{name} '%{public_key}' <server directory>
+
+		Once it is registered, you can start the assembly worker like so:
+		  assemblage start worker %{directory}
+
+	}.gsub( /^\t+/, '' )
+
+
 
-	desc "Add connection information to a run directory."
-	command :add do |add|
+	desc "Worker commands"
+	command :worker do |worker|
+
+		worker.desc 'Set up a new assembly worker'
+		worker.long_desc <<-END_DESC
+		Set up a new assembly worker in the given DIRECTORY. If the DIRECTORY is
+		not specified, the current directory will be used. If the target directory
+		is not empty, this command will abort.
+		END_DESC
+		worker.arg :DIRECTORY
+		worker.command :create do |create|
+
+			create.desc "Specify a name that will identify the worker on any " +
+				"servers it registers with"
+			create.flag [:N, :name], type: String,
+				must_match: Assemblage::Auth::CLIENT_NAME_PATTERN
+
+			create.desc "Specify one or more tags that indicate what assemblies " +
+				"the worker should accept"
+			create.flag [:t, :tags], type: Array
+
+			create.action do |globals, options, args|
+				directory = Pathname( args.shift || '.' ).expand_path
+
+				name = options.name || "%s-%s" %
+					[ Socket.gethostname.downcase.gsub('.', '-'), directory.basename ]
+				tags = options.tags
+
+				prompt.say "Creating a worker run directory in %s..." % [ directory ]
+				Assemblage::Worker.setup_run_directory( directory, name, tags )
 
-		add.desc "Add a new worker to a server"
-		add.long_desc <<-END_DESC
+				prompt.say "Generating a worker key..."
+				Assemblage::Worker.generate_cert
+
+				msg = CREATE_ADVICE % {
+					name: name,
+					public_key: Assemblage::Worker.public_key,
+					directory: directory
+				}
+				prompt.say( msg )
+			end
+		end
+
+
+		worker.desc "Output the worker's public key"
+		worker.long_desc <<-END_DESC
+		Output the worker's public key to STDOUT.
+		END_DESC
+		worker.arg :DIRECTORY, :optional
+		worker.command 'show-key' do |show_key|
+			show_key.action do |globals, options, args|
+				directory = Pathname( args.shift || '.' ).expand_path
+
+				Assemblage.use_run_directory( directory )
+				prompt.say "Worker's public key:"
+				puts Assemblage::Worker.public_key
+			end
+		end
+
+
+		worker.desc "Add a new worker to a server"
+		worker.long_desc <<-END_DESC
 		Add a cert and configuration for a new worker to a server run directory.
 		END_DESC
-		add.arg :NAME
-		add.arg :PUBLIC_KEY
-		add.arg :SERVER_DIRECTORY, :optional
-		add.command :worker do |worker|
+		worker.arg :NAME
+		worker.arg :PUBLIC_KEY
+		worker.arg :SERVER_DIRECTORY, :optional
+		worker.command :add do |add|
 
-			worker.action do |globals, options, args|
+			add.action do |globals, options, args|
 				name = args.shift or help_now!( "Missing the worker name." )
 				public_key = args.shift or help_now!( "Missing the worker public key." )
 

          
@@ 36,26 103,19 @@ module Assemblage::CLI::AddServer
 		end
 
 
-		add.desc "Add a new server to a worker"
-		add.long_desc <<-END_DESC
-		Add a cert and configuration for a new server to a worker run directory.
+		worker.desc 'Start an assembly worker'
+		worker.long_desc <<-END_DESC
+		Start an Assemblage worker in the specified DIRECTORY. If not specified, the
+		DIRECTORY will default to the current working directory.
 		END_DESC
-		add.arg :URL
-		add.arg :PUBLIC_KEY
-		add.arg :WORKER_DIRECTORY, :optional
-		add.command :server do |server|
-
-			server.action do |globals, options, args|
-				url = args.shift or help_now!( "Missing the server url." )
-				public_key = args.shift or help_now!( "Missing the server public key." )
-
-				Assemblage.use_run_directory( args.shift )
-				Assemblage::Worker.add_server( url, public_key )
-
-				prompt.say "done."
+		worker.arg :DIRECTORY, :optional
+		worker.command :start do |start|
+			start.action do |globals, options, args|
+				Assemblage::Worker.run( args.shift )
 			end
 		end
 
+
 	end
 
 end # module Assemblage::CLI::AddServer

          
M lib/assemblage/repository.rb +3 -2
@@ 6,6 6,8 @@ require 'assemblage/db_object'
 
 
 class Assemblage::Repository < Assemblage::DbObject( :repositories )
+	extend Configurability
+
 
 	# Maintain the timestamp fields automatically
 	plugin :timestamps

          
@@ 35,7 37,7 @@ class Assemblage::Repository < Assemblag
 
 	### Set up the +directory+ as a repo run directory. Raises an
 	### exception if the directory already exists and is not empty.
-	def self::setup_run_directory( name, directory='.' )
+	def self::setup_run_directory( directory='.' )
 		directory = Pathname( directory || '.' )
 		raise "Directory not empty" if directory.exist? && !directory.empty?
 

          
@@ 45,7 47,6 @@ class Assemblage::Repository < Assemblag
 
 		config = Assemblage.config || Configurability.default_config
 		config.assemblage.auth.cert_store_dir ||= (directory + 'certs').to_s
-		config.assemblage.repo.name = name
 
 		Loggability.with_level( :fatal ) do
 			config.install

          
M lib/assemblage/server.rb +8 -0
@@ 120,6 120,14 @@ class Assemblage::Server
 	end
 
 
+	### Add a repository with the specified +name+ and +public_key+ to the current run
+	### directory.
+	def self::add_repo( name, public_key )
+		self.log.info "Adding repo %p with public key: %s" % [ name, public_key ]
+		cert = Assemblage::Auth.save_remote_cert( name, public_key )
+	end
+
+
 	### Run an instance of the server from the specified +run_directory+.
 	def self::run( run_directory=nil, **options )
 		Assemblage.use_run_directory( run_directory, reload_config: true )