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']