Checkpoint commit
M .assembly/on_commit-00_quality_check => .assembly/commit/on_commit-00_quality_check +0 -0

M .assembly/on_commit-01_test => .assembly/commit/on_commit-01_test +0 -0

R .assembly/on_tag-02_release =>  +0 -3
@@ 1,3 0,0 @@ 
-#!/usr/bin/env bash
-
-rake release

          
M .assembly/on_tag-00_check_signature => .assembly/tag/on_tag-00_check_signature +0 -0

M .assembly/on_tag-01_test => .assembly/tag/on_tag-01_test +0 -0

M .assembly/on_tag-01_test => .assembly/tag/on_tag-02_release +0 -0

M .assembly/on_tag-03_publish_docs => .assembly/tag/on_tag-03_publish_docs +0 -0

M .hgignore +2 -0
@@ 6,3 6,5 @@ coverage/
 \.yml$
 spec/.status
 \.lock$
+\.db$
+

          
A => Cookbook.md +29 -0
@@ 0,0 1,29 @@ 
+# Assemblage Cookbook
+
+### Run Tests On Every Commit in a Single Repo
+
+    <repo>/.assemblies/commit/00_run_tests.sh
+
+
+### Mail a Notification On Every Release
+
+    /usr/local/assemblies/__global___/tag/00_mail_release_notice.sh
+
+
+### Build gem for Every Release for Every Ruby Repo
+
+    /usr/local/assemblies/<trait>/tag/00_run_tests.sh
+
+
+
+
+<repo>/.assemblies/config.yml
+
+---
+traits:
+  - ruby
+
+requirements:
+  imagemagick: 6
+  ruby: 2.6
+

          
M Manifest.txt +27 -3
@@ 1,31 1,55 @@ 
+.assembly/commit/on_commit-00_quality_check
+.assembly/commit/on_commit-01_test
+.assembly/tag/on_tag-00_check_signature
+.assembly/tag/on_tag-01_test
+.assembly/tag/on_tag-02_release
+.assembly/tag/on_tag-03_publish_docs
 .document
 .editorconfig
 .rdoc_options
 .simplecov
 ChangeLog
+Cookbook.md
 History.md
 LICENSE.txt
 Manifest.txt
+Protocol.md
 README.md
 Rakefile
+To-Do.md
 bin/assemblage
+data/assemblage/hooks/mercurial-hook.rb
 data/assemblage/migrations/20180314_initial.rb
 lib/assemblage.rb
+lib/assemblage/assembly_builder.rb
+lib/assemblage/assembly_result.rb
 lib/assemblage/auth.rb
 lib/assemblage/cli.rb
 lib/assemblage/client.rb
-lib/assemblage/command/add.rb
-lib/assemblage/command/create.rb
-lib/assemblage/command/start.rb
+lib/assemblage/command/client.rb
+lib/assemblage/command/publisher.rb
+lib/assemblage/command/server.rb
+lib/assemblage/command/worker.rb
+lib/assemblage/connection.rb
 lib/assemblage/db_object.rb
+lib/assemblage/event.rb
 lib/assemblage/mixins.rb
 lib/assemblage/protocol.rb
+lib/assemblage/publisher.rb
+lib/assemblage/refinements.rb
+lib/assemblage/repository.rb
 lib/assemblage/server.rb
+lib/assemblage/vcs_strategy.rb
+lib/assemblage/vcs_strategy/mercurial.rb
 lib/assemblage/worker.rb
+lib/cztop/monkeypatches.rb
+local.db
 spec/.status
 spec/assemblage/auth_spec.rb
+spec/assemblage/client_spec.rb
 spec/assemblage/mixins_spec.rb
 spec/assemblage/server_spec.rb
+spec/assemblage/vcs_strategy_spec.rb
 spec/assemblage/worker_spec.rb
 spec/assemblage_spec.rb
 spec/spec_helper.rb

          
M README.md +10 -1
@@ 70,17 70,26 @@ Server**, **Assembly Workers**, and **Pu
 
 ## Installation
 
+
+## Usage
+
 This example uses three different hosts for the three parts, but you can, of
 course, run all of this on a single host.
 
 ...
 
 
-Now copy the Mercurial hook to the repository and hook it into the .hg/hgrc:
+Now copy the Mercurial hook to the repository (or another directory) and hook it into the .hg/hgrc:
 
     [hooks]
     incoming.assemblage = assemblage-hook.rb
+    changegroup.assemblage = assemblage-hook.rb
+    commit.assemblage = assemblage-hook.rb
     incoming.assemblage = assemblage-hook.rb
+    txnclose-bookmark.assemblage = assemblage-hook.rb
+    txnclose-phase.assemblage = assemblage-hook.rb
+    pushkey.assemblage = assemblage-hook.rb
+    tag.assemblage = assemblage-hook.rb
 
 And finally, we'll combine all the parts into an assembly called
 `project1-freebsd-tests` that will run on a worker with the `freebsd`, `ruby`,

          
A => data/assemblage/hooks/mercurial-hook.rb +85 -0
@@ 0,0 1,85 @@ 
+#!/usr/bin/env ruby
+
+require 'bundler/setup'
+require 'pathname'
+require 'hglib'
+require 'assemblage'
+require 'assemblage/cli'
+
+BASEDIR = Pathname( __FILE__ ).dirname
+CONFIG = BASEDIR + 'config.yml'
+
+Assemblage.load_config( CONFIG )
+
+client = Assemblage::Client.new_by_name( 'server' )
+hgenv = ENV.
+	filter {|k,v| k.starts_with?('HG_') }.
+	each_with_object( {} ) do |hash, (name, val)|
+		hash[ name.downcase.to_sym ] = val
+	end
+
+$stderr.puts "Publishing a %p event." % [ hgenv[:hg_hooktype] ]
+case hgenv[ :hg_hooktype ]
+
+# Run after a changegroup has been added via push, pull or unbundle. The ID of
+# the first new changeset is in "$HG_NODE" and last is in "$HG_NODE_LAST". The
+# URL from which changes came is in "$HG_URL".
+when 'changegroup'
+	client.send_message( :event, 'commit', hgenv[:hg_last_node], **hgenv )
+
+# Run after a changeset has been created in the local repository. The ID of
+# the newly created changeset is in "$HG_NODE". Parent changeset IDs are in
+# "$HG_PARENT1" and "$HG_PARENT2".
+when 'commit'
+	client.send_message( :event, 'commit', hgenv[:hg_node], **hgenv )
+
+# Run after a changeset has been pulled, pushed, or unbundled into the local
+# repository. The ID of the newly arrived changeset is in "$HG_NODE". The URL
+# that was source of the changes is in "$HG_URL".
+when 'incoming'
+	client.send_message( :event, 'commit', hgenv[:hg_node], **hgenv )
+
+# Run after any bookmark change has been committed. At this point, the
+# transaction can no longer be rolled back. The hook will run after the lock is
+# released. The name of the bookmark will be available in "$HG_BOOKMARK", the
+# new bookmark location will be available in "$HG_NODE" while the previous
+# location will be available in "$HG_OLDNODE". In case of a bookmark creation
+# "$HG_OLDNODE" will be empty. In case of deletion "$HG_NODE" will be empty. In
+# addition, the reason for the transaction opening will be in "$HG_TXNNAME", and
+# a unique identifier for the transaction will be in "HG_TXNID".
+when 'txnclose-bookmark'
+	client.send_message( :event, 'bookmark', hgenv[:hg_node], hgenv[:hg_bookmark], **hgenv )
+
+# Run after any phase change has been committed. At this point, the transaction
+# can no longer be rolled back. The hook will run after the lock is released.
+# The affected node is available in "$HG_NODE", the phase in "$HG_PHASE" while
+# the previous "$HG_OLDPHASE". In case of new node, "$HG_OLDPHASE" will be
+# empty. In addition, the reason for the transaction opening will be in
+# "$HG_TXNNAME", and a unique identifier for the transaction will be in
+# "HG_TXNID". The hook is also run for newly added revisions. In this case the
+# "$HG_OLDPHASE" entry will be empty.
+when 'txnclose-phase'
+	client.send_message( :event, 'phase', hgenv[:hg_node], hgenv[:hg_oldphase],
+		hgenv[:hg_phase], **hgenv )
+
+# Run after a pushkey (like a bookmark) is added to the repository. The key
+# namespace is in "$HG_NAMESPACE", the key is in "$HG_KEY", the old value (if
+# any) is in "$HG_OLD", and the new value is in "$HG_NEW".;
+when 'pushkey'
+	client.send_message( :event, 'pushkey', hgenv[:hg_namespace], hgenv[:hg_key],
+		hgenv[:hg_new], **hgenv )
+
+# Run after a tag is created. The ID of the tagged changeset is in "$HG_NODE".
+# The name of tag is in "$HG_TAG". The tag is local if "$HG_LOCAL=1", or in the
+# repository if "$HG_LOCAL=0".
+when 'tag'
+	if hgenv[:hg_local] == '0'
+		client.send_message( :event, 'tag', hgenv[:hg_node], hgenv[:hg_tag], **hgenv )
+	end
+end
+
+client.start do |type, data, **header|
+	$stderr.puts "%s response: %p" % [ type, data ]
+end
+
+

          
M data/assemblage/migrations/20180314_initial.rb +4 -3
@@ 11,6 11,9 @@ Sequel.migration do
 			String :type, null: false
 			String :url, null: false, unique: true
 
+			jsonb :requirements, default: "{}"
+			jsonb :traits, default: "[]"
+
 			Time :created_at, default: Sequel.function(:now)
 			Time :updated_at
 			Time :removed_at

          
@@ 36,7 39,7 @@ Sequel.migration do
 			primary_key :id
 
 			String :type, null: false
-			String :identifier, null: false
+			jsonb :data, default: "{}"
 
 			Time :created_at, default: Sequel.function(:now)
 			Time :updated_at

          
@@ 45,8 48,6 @@ Sequel.migration do
 			foreign_key :repository_id, :repositories, null: false,
 				on_delete: :cascade
 			foreign_key :via_connection_id, :connections, null: false
-
-			unique [:type, :identifier, :repository_id]
 		end
 
 

          
M lib/assemblage.rb +7 -3
@@ 1,6 1,7 @@ 
 # -*- ruby -*-
-#encoding: utf-8
+# frozen_string_literal: true
 
+require 'pathname'
 require 'securerandom'
 require 'socket'
 require 'set'

          
@@ 44,16 45,19 @@ module Assemblage
 
 	# Autoload subordinate modules
 	autoload :AssemblyBuilder, 'assemblage/assembly_builder'
+	autoload :AssemblyResult, 'assemblage/assembly_result'
 	autoload :Auth, 'assemblage/auth'
 	autoload :CLI, 'assemblage/cli'
+	autoload :Client, 'assemblage/client'
+	autoload :Connection, 'assemblage/connection'
 	autoload :DbObject, 'assemblage/db_object'
+	autoload :Event, 'assemblage/event'
 	autoload :Protocol, 'assemblage/protocol'
 	autoload :Publisher, 'assemblage/publisher'
 	autoload :Repository, 'assemblage/repository'
 	autoload :Server, 'assemblage/server'
+	autoload :VCSStrategy, 'assemblage/vcs_strategy'
 	autoload :Worker, 'assemblage/worker'
-	autoload :VCSStrategy, 'assemblage/vcs_strategy'
-
 
 	require 'assemblage/mixins'
 	extend Assemblage::MethodUtilities

          
M lib/assemblage/auth.rb +2 -2
@@ 1,5 1,5 @@ 
 # -*- ruby -*-
-#encoding: utf-8
+# frozen_string_literal: true
 
 require 'cztop'
 require 'configurability'

          
@@ 267,7 267,7 @@ module Assemblage::Auth
 			self.log.info "Creating CURVE authenticator."
 			auth = CZTop::Authenticator.new
 
-			if certs_dir = self.remote_certs_path && ! self.allow_registration
+			if (certs_dir = self.remote_certs_path) && ! self.allow_registration
 				self.log.info "Using remote certs dir %s for auth." % [ certs_dir ]
 				certs_dir.mkpath
 				auth.curve( certs_dir.to_s )

          
M lib/assemblage/client.rb +23 -4
@@ 120,6 120,21 @@ class Assemblage::Client
 	include CZTop::Reactor::SocketMonitoring
 
 
+	### Create a new Client that will connect to the server whose cert is in the
+	### cert repository under the specified +cert_name+. This requires that the
+	### current config have both a local cert set up as well as a remote cert with
+	### an endpoint.
+	def self::new_by_name( cert_name, &msg_handler )
+		client_cert = Assemblage::Auth.local_cert
+		server_cert = Assemblage::Auth.remote_cert( cert_name ) or
+			raise "No such cert %p" % [ cert_name ]
+		endpoint = server_cert[ 'endpoint' ] or
+			raise "The %s cert does not have an endpoint in its metadata!" % [ cert_name ]
+
+		return new( endpoint, client_cert, server_cert, &msg_handler )
+	end
+
+
 	### Create a client that will communicate with the server at the specified
 	### +endpoint+. It will used the given +client_cert+ and +server_cert+ for ZAUTH
 	### authentication, and if +msg_handler+ is provided, it will be used as the

          
@@ 184,8 199,11 @@ class Assemblage::Client
 	attr_predicate :running
 
 
-	### Start the client
-	def start
+	### Start the client. If called with a block, the block overrides the default
+	### message handler the client was created with, if any.
+	def start( &msg_handler )
+		self.message_handlers.default = msg_handler if msg_handler
+
 		self.log.info "Starting up..."
 		self.register_default_handlers
 

          
@@ 220,6 238,7 @@ class Assemblage::Client
 
 	### Send a message to the server.
 	def send_message( verb, *data, **header )
+		data.flatten!( 1 )
 		self.input_queue << Assemblage::Protocol.encode( verb.to_sym, data, **header )
 		self.register_for_writing if self.reactor.registered?( self.socket )
 	end

          
@@ 337,14 356,14 @@ class Assemblage::Client
 
 
 	### Handle an error control message from the server.
-	def handle_error_message( data, header )
+	def handle_error_message( type, data, header )
 		data = data.join( "\n" ) if data.respond_to?( :join )
 		self.log.error "Got an error response from server: %s" % [ data ]
 	end
 
 
 	### Handle an informational message from the server.
-	def handle_info_message( data, header )
+	def handle_info_message( type, data, header )
 		data = data.join( "\n" ) if data.respond_to?( :join )
 		self.log.info "Got an info message from the server: %s" % [ data ]
 	end

          
M lib/assemblage/command/publisher.rb +3 -3
@@ 10,7 10,7 @@ module Assemblage::CLI::PublisherCommand
 	extend Assemblage::CLI::Subcommand
 
 
-	CREATE_ADVICE = %{
+	CREATE_ADVICE = <<~END_ADVICE
 		Now you can approve this publisher for the server like so:
 		  assemblage server approve-publisher %{name} <server directory>
 

          
@@ 20,7 20,7 @@ module Assemblage::CLI::PublisherCommand
 		[hooks]
 		incoming.myrepo = assemblage %{directory}/config.yml publish commit %{name} $HG_NODE
 
-	}.gsub( /^\t+/, '' )
+	END_ADVICE
 
 
 	desc "Publisher commands"

          
@@ 120,7 120,7 @@ module Assemblage::CLI::PublisherCommand
 
 		publisher.desc "Publish an event from a repo."
 		publisher.long_desc <<~END_DESC
-		Publish the specified EVENT 
+		Publish the specified EVENT
 		END_DESC
 		publisher.arg :URL
 		publisher.arg :PUBLIC_KEY

          
R lib/assemblage/command/repo.rb =>  +0 -14
@@ 1,14 0,0 @@ 
-# -*- ruby -*-
-# frozen_string_literal: true
-
-require 'assemblage/cli' unless defined?( Assemblage::CLI )
-
-
-# Assemblage Repository commands
-module Assemblage::CLI::RepoCommand
-	extend Assemblage::CLI::Subcommand
-
-
-
-end # module Assemblage::CLI::RepoCommand
-

          
M lib/assemblage/command/server.rb +8 -8
@@ 9,14 9,14 @@ require 'assemblage/server'
 module Assemblage::CLI::ServerCommand
 	extend Assemblage::CLI::Subcommand
 
-	CREATE_ADVICE = %{
+	CREATE_ADVICE = <<~END_ADVICE
 		You can start the assembly server like so:
 		  assemblage server start %{directory}
 
 		Server public key is:
 		  %{public_key}
 
-	}.gsub( /^\t+/, '' )
+	END_ADVICE
 
 	# The range of ports to choose from if an endpoint isn't specified when creating
 	# a new server.

          
@@ 128,7 128,7 @@ module Assemblage::CLI::ServerCommand
 					exit_now! "Connections from %s are already approved." % [ name ]
 				else
 					prompt.say "Approving connections from %s..." % [ name ]
-					Assemblage::Server.approve_connections( name, :worker )
+					Assemblage::Server.approve_connections_from( name, :worker )
 					prompt.say "done."
 				end
 			end

          
@@ 150,7 150,7 @@ module Assemblage::CLI::ServerCommand
 				Assemblage.use_run_directory( args.shift )
 
 				prompt.say "Approving connections from %s..." % [ name ]
-				Assemblage::Server.approve_connections( name, :publisher )
+				Assemblage::Server.approve_connections_from( name, :publisher )
 				prompt.say "done."
 			end
 

          
@@ 158,12 158,12 @@ module Assemblage::CLI::ServerCommand
 
 
 		server.desc "Add a new repository to a server"
-		server.long_desc <<-END_DESC
+		server.long_desc <<~END_DESC
 		Add a new repository named NAME and type TYPE to the server config in SERVER_DIRECTORY.
 		Workers will check out source from the specified URL in response to events
-		published from it.  
-	  
-		Valid types are:  
+		published from it.
+
+		Valid types are:
 		  #{Assemblage::VCSStrategy.available.map(&:to_s).join(', ')}
 		END_DESC
 		server.arg :NAME

          
M lib/assemblage/command/worker.rb +2 -2
@@ 16,14 16,14 @@ require 'assemblage/worker'
 module Assemblage::CLI::WorkerCommand
 	extend Assemblage::CLI::Subcommand
 
-	CREATE_ADVICE = %{
+	CREATE_ADVICE = <<~END_ADVICE
 		Now you can approve this worker for the server like so:
 		  assemblage server approve-worker %{name} <server directory>
 
 		Once it is approved, you can start the assembly worker like so:
 		  assemblage worker start %{directory}
 
-	}.gsub( /^\t+/, '' )
+	END_ADVICE
 
 
 

          
R lib/assemblage/commit.rb =>  +0 -27
@@ 1,27 0,0 @@ 
-# -*- ruby -*-
-# frozen_string_literal: true
-
-require 'assemblage' unless defined?( Assemblage )
-require 'assemblage/db_object'
-
-
-class Assemblage::Commit < Assemblage::DbObject( :assemblies )
-
-	# Maintain the timestamp fields automatically
-	plugin :timestamps
-
-	#
-	# Associations
-	#
-
-	##
-	# Results for this assembly
-	one_to_many :results, class: 'Assemblage::AssemblyResult'
-
-	##
-	# The Repository this assembly is for
-	many_to_one :repository, class: 'Assemblage::Respository'
-
-
-end # class Assemblage::Commit
-

          
A => lib/assemblage/event.rb +23 -0
@@ 0,0 1,23 @@ 
+# -*- ruby -*-
+# frozen_string_literal: true
+
+require 'assemblage' unless defined?( Assemblage )
+require 'assemblage/db_object'
+require 'assemblage/protocol'
+
+
+class Assemblage::Event < Assemblage::DbObject( Sequel[:events] )
+
+	# Maintain the timestamp fields automatically
+	plugin :timestamps
+
+	# Serialize the `data` column
+	plugin :serialization, :json, :data
+
+	##
+	# The repository the event occurred in
+	many_to_one :repository, class: 'Assemblage::Repository'
+
+
+end # class Assemblage::Event
+

          
M lib/assemblage/protocol.rb +2 -2
@@ 1,5 1,5 @@ 
 # -*- ruby -*-
-#encoding: utf-8
+# frozen_string_literal: true
 
 require 'e2mmap'
 require 'msgpack'

          
@@ 109,7 109,7 @@ module Assemblage::Protocol
 	end
 
 
-	### Construct a STATUS message from the specified 
+	### Construct a STATUS message from the specified
 	def status
 		return Assemblage::Protocol.encode( :status )
 	end

          
M lib/assemblage/repository.rb +7 -0
@@ 11,6 11,9 @@ class Assemblage::Repository < Assemblag
 	# Maintain the timestamp fields automatically
 	plugin :timestamps
 
+	# Serialize the requirements and traits columns
+	plugin :serialization, :json, :requirements, :traits
+
 	#
 	# Associations
 	#

          
@@ 19,5 22,9 @@ class Assemblage::Repository < Assemblag
 	# Assemblies for this repository
 	one_to_many :assemblies, class: 'Assemblage::Assembly'
 
+	##
+	# Assemblies for this repository
+	one_to_many :events, class: 'Assemblage::Event'
+
 end # class Assemblage::Repository
 

          
M lib/assemblage/server.rb +43 -8
@@ 38,6 38,7 @@ class Assemblage::Server
 	AUTHED_COMMANDS = %i[
 		status
 		status_report
+		event
 	]
 
 	# The list of valid commands for unauthenticated connections

          
@@ 72,6 73,13 @@ class Assemblage::Server
 	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 )
+		return self.new( **options ).run
+	end
+
+
 	### Set up the Server's directory as an Assemblage run directory, and return the
 	### resulting config object (a Configurability::Config). Raises an
 	### exception if the directory already exists and is not empty.

          
@@ 120,15 128,19 @@ class Assemblage::Server
 
 
 	### Approve connections from the client with the specified +name+ and
-	### +public_key+ to the current run directory.
-	def self::approve_connections( name, type=:worker )
+	### +public_key+ to the server configured in the current run directory.
+	def self::approve_connections_from( name, type=:worker )
 		registered_type = self.connection_type( name )
 		raise "Cert for %p is not a %s cert." % [ name, type ] unless
 			registered_type == type.to_s
 
 		self.log.info "Approving %s %p" % [ type, name ]
-		conn = Assemblage::Connection.create( type: type.to_s, name: name )
-		self.log.debug "  created connection %d" % [ conn.pk ]
+		conn = Assemblage::Connection.find_or_create( type: type.to_s, name: name ) do
+			self.log.debug "  creating connection..."
+		end
+		self.log.debug "  connection approved at %s" % [ conn.created_at ]
+
+		return conn
 	end
 
 

          
@@ 148,10 160,20 @@ class Assemblage::Server
 	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 )
-		return self.new( **options ).run
+	### Add a new repository to the server configured in the current run directory.
+	def self::add_repo( name, type, url )
+		unless Assemblage::VCSStrategy.available.include?( type )
+			raise "Invalid repo type %p; supported types are: %s" %
+				[ type, Assemblage::VCSStrategy.available.join(', ') ]
+		end
+
+		repo = Assemblage::Repository.find_or_create( name: name ) do |repo|
+			self.log.info "Adding %s repo %s at %s" % [ type, name, url ]
+			repo.type = type
+			repo.url = url
+		end
+
+		return repo
 	end
 
 

          
@@ 279,6 301,19 @@ class Assemblage::Server
 	# Command methods
 	#
 
+	### Handle an `event` command for the specified +connection+.
+	def handle_event_command( connection, data, header )
+		event_type, repo_name, event_data = *data
+
+		repo = Assemblage::Repository[ name: repo_name ] or
+			raise "No such repository %p" % [ repo_name ]
+		event = repo.add_event( type: event_type, data: event_data, via_connection_id: connection.id )
+
+		msg = connection.response( :event_added, [event.id] )
+		self.queue_output_message( msg )
+	end
+
+
 	### Handle a `status` command for the specified +connection+.
 	def handle_status_command( connection, * )
 		status = { version: Assemblage::VERSION, uptime: self.uptime }

          
R lib/assemblage/user.rb =>  +0 -23
@@ 1,23 0,0 @@ 
-# -*- ruby -*-
-# frozen_string_literal: true
-
-require 'rbnacl'
-
-require 'assemblage' unless defined?( Assemblage )
-require 'assemblage/db_object'
-
-
-class Assemblage::User < Assemblage::DbObject( :users )
-
-
-	# Maintain the timestamp fields automatically
-	plugin :timestamps
-
-	#
-	# Associations
-	#
-
-end # class Assemblage::User
-
-
-

          
M lib/assemblage/worker.rb +1 -1
@@ 304,7 304,7 @@ class Assemblage::Worker
 
 
 	### Return a Hash of the criteria the worker should use when
-	### requesting/subscribing to jobs frmo the server.
+	### requesting/subscribing to jobs from the server.
 	def job_criteria
 		platform = Gem::Platform.local
 		return {

          
A => spec/assemblage/client_spec.rb +15 -0
@@ 0,0 1,15 @@ 
+#!/usr/bin/env rspec -cfd
+
+require_relative '../spec_helper'
+
+require 'assemblage/client'
+
+
+describe Assemblage::Client do
+
+	it "is well-tested" do
+		fail "it isn't."
+	end
+
+end
+

          
M spec/assemblage/server_spec.rb +0 -40
@@ 80,46 80,6 @@ describe Assemblage::Server, db: true do
 		end
 
 
-		describe "the status command" do
-
-			fit "returns a Map with information about the server" do
-				config = described_class.setup_run_directory( @assemblage_dir )
-				described_class.generate_cert
-				described_class.create_database
-
-				Assemblage.use_run_directory( @assemblage_dir )
-				Loggability.level = :debug
-				Loggability.format_with( :color )
-
-				described_class.add_worker( 'testworker1', @worker_cert.public_key )
-
-				@server = described_class.new
-				@server_thread = Thread.new do
-					Thread.current.abort_on_exception = true
-					@server.run
-				end
-				wait_time = 0
-				wait_time += sleep( 0.1 ) until @server.running? || wait_time > 2.0
-				raise "Server didn't start" unless @server.running?
-
-				begin
-					msg = Assemblage::Protocol.encode( :status, [] )
-
-					msg.send_to( sock )
-					resmsg = sock.receive
-
-					hdr, body = Assemblage::Protocol.decode( resmsg )
-					expect( hdr ).to include( 'success' => true )
-					expect( body.length ).to eq( 4 )
-					expect( body ).to include( 'server_version', 'state', 'uptime' )
-				ensure
-					@server.stop if @server
-					@server_thread.kill unless !@server_thread || @server_thread.join( 2 )
-				end
-			end
-
-		end
-
 	end
 
 

          
M spec/assemblage_spec.rb +1 -1
@@ 1,5 1,5 @@ 
 #!/usr/bin/env rspec -cfd
-#encoding: utf-8
+# frozen_string_literal: true
 
 require_relative 'spec_helper'
 

          
M spec/spec_helper.rb +1 -1
@@ 1,5 1,5 @@ 
 # -*- ruby -*-
-#encoding: utf-8
+# frozen_string_literal: true
 
 require 'simplecov' if ENV['COVERAGE']