From f4889f6079cf2bf1d016fa8b2fa39056097a994a Mon Sep 17 00:00:00 2001 From: Mike Virata-Stone Date: Wed, 4 Nov 2015 13:56:11 -0800 Subject: [PATCH 1/7] Remove SecureRandom usage This test doesn't seem to really need random data, just arbitrary data After reading https://github.com/jruby/jruby/wiki/Improving-startup-time#ensure-your-system-has-adequate-entropy, I think we should avoid using SecureRandom except where we really need it --- spec/gemstash/storage_spec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/gemstash/storage_spec.rb b/spec/gemstash/storage_spec.rb index 82cacb48..91759287 100644 --- a/spec/gemstash/storage_spec.rb +++ b/spec/gemstash/storage_spec.rb @@ -1,5 +1,4 @@ require "spec_helper" -require "securerandom" describe Gemstash::Storage do before do @@ -67,8 +66,8 @@ end context "with a previously stored resource" do - let(:resource_id) { SecureRandom.uuid } - let(:content) { SecureRandom.base64 } + let(:resource_id) { "42" } + let(:content) { "zapatito" } before do storage.resource(resource_id).save(content) end From c8a0a4c595488dbb219635aac52e86d6a3e4be58 Mon Sep 17 00:00:00 2001 From: Mike Virata-Stone Date: Wed, 4 Nov 2015 15:53:47 -0800 Subject: [PATCH 2/7] Monkeypatch bundler to not use SecureRandom when running on JRuby This along with --dev significantly improves spec duration with JRuby --- spec/support/exec_helpers.rb | 16 ++++++++++++++-- spec/support/jruby_bundler_monkeypatch.rb | 13 +++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 spec/support/jruby_bundler_monkeypatch.rb diff --git a/spec/support/exec_helpers.rb b/spec/support/exec_helpers.rb index aaea9b16..46646a08 100644 --- a/spec/support/exec_helpers.rb +++ b/spec/support/exec_helpers.rb @@ -1,3 +1,4 @@ +require "bundler/version" require "open3" # Helpers for executing commands and asserting the results. @@ -10,12 +11,23 @@ def execute(command, dir: nil, env: {}) bundle_gemfile = gemfile_path if File.exist?(gemfile_path) end - env = { + default_env = { "BUNDLE_GEMFILE" => bundle_gemfile, "RUBYLIB" => nil, "RUBYOPT" => nil, "GEM_PATH" => ENV["_ORIGINAL_GEM_PATH"] - }.merge(env) + } + + if RUBY_PLATFORM == "java" + default_env["JRUBY_OPTS"] = "--dev" + + if command.start_with?("bundle") + bundler_patch = File.expand_path("../jruby_bundler_monkeypatch.rb", __FILE__) + default_env["JRUBY_OPTS"] += " -r#{bundler_patch}" + end + end + + env = default_env.merge(env) dir ||= File.expand_path(".") Result.new(env, command, dir) end diff --git a/spec/support/jruby_bundler_monkeypatch.rb b/spec/support/jruby_bundler_monkeypatch.rb new file mode 100644 index 00000000..9c0e84df --- /dev/null +++ b/spec/support/jruby_bundler_monkeypatch.rb @@ -0,0 +1,13 @@ +require "bundler" +require "bundler/fetcher" + +module Bundler + #:nodoc: + class Fetcher + # The Bundler user_agent uses SecureRandom, which causes the specs + # to run out of entropy and run a lot longer than they need to. + def user_agent + "gemstash spec" + end + end +end From b67653863fec59beea9771907e69c26ebf7cdeb7 Mon Sep 17 00:00:00 2001 From: Mike Virata-Stone Date: Wed, 4 Nov 2015 16:42:06 -0800 Subject: [PATCH 3/7] Break up massive execute method into smaller chunks inside the Result class --- spec/support/exec_helpers.rb | 65 +++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/spec/support/exec_helpers.rb b/spec/support/exec_helpers.rb index 46646a08..35fba0b5 100644 --- a/spec/support/exec_helpers.rb +++ b/spec/support/exec_helpers.rb @@ -4,31 +4,6 @@ # Helpers for executing commands and asserting the results. module ExecHelpers def execute(command, dir: nil, env: {}) - bundle_gemfile = nil - - if dir - gemfile_path = File.join(dir, "Gemfile") - bundle_gemfile = gemfile_path if File.exist?(gemfile_path) - end - - default_env = { - "BUNDLE_GEMFILE" => bundle_gemfile, - "RUBYLIB" => nil, - "RUBYOPT" => nil, - "GEM_PATH" => ENV["_ORIGINAL_GEM_PATH"] - } - - if RUBY_PLATFORM == "java" - default_env["JRUBY_OPTS"] = "--dev" - - if command.start_with?("bundle") - bundler_patch = File.expand_path("../jruby_bundler_monkeypatch.rb", __FILE__) - default_env["JRUBY_OPTS"] += " -r#{bundler_patch}" - end - end - - env = default_env.merge(env) - dir ||= File.expand_path(".") Result.new(env, command, dir) end @@ -38,8 +13,10 @@ class Result def initialize(env, command, dir) @command = command - @dir = dir - @output, @status = Open3.capture2e(env, command, chdir: dir) + @env = env + @original_dir = dir + @dir = dir || File.expand_path(".") + @output, @status = Open3.capture2e(patched_env, @command, chdir: @dir) fix_jruby_output end @@ -65,6 +42,40 @@ def fix_jruby_output @output.gsub!(/^.*warning: unsupported exec option: close_others\n/, "") end + + def patched_env + @patched_env ||= faster_jruby_env.merge(clear_bundler_env).merge(clear_ruby_env).merge(@env) + end + + def clear_ruby_env + { + "RUBYLIB" => nil, + "RUBYOPT" => nil, + "GEM_PATH" => ENV["_ORIGINAL_GEM_PATH"] + } + end + + def clear_bundler_env + if @original_dir + gemfile_path = File.join(@original_dir, "Gemfile") + bundle_gemfile = gemfile_path if File.exist?(gemfile_path) + end + + { "BUNDLE_GEMFILE" => bundle_gemfile } + end + + def faster_jruby_env + return {} unless RUBY_PLATFORM == "java" + jruby_opts = "--dev" + + if command.start_with?("bundle") + bundler_patch = File.expand_path("../jruby_bundler_monkeypatch.rb", __FILE__) + raise "Spaces in your path will cause build failures!" if bundler_patch.include?(" ") + jruby_opts += " -r#{bundler_patch}" + end + + { "JRUBY_OPTS" => jruby_opts } + end end end From d5f4a1dee74e8d081141b1e13bbc4fc54463ee6e Mon Sep 17 00:00:00 2001 From: Mike Virata-Stone Date: Thu, 5 Nov 2015 00:26:43 -0800 Subject: [PATCH 4/7] URI escape SQLite DB path in case of spaces (failures otherwise) Don't rely on current directory... it could include a relative path with a space, which could cause issues Use relative path to JRuby Bundler monkeypatch, so spaces won't cause a problem --- lib/gemstash/env.rb | 3 ++- spec/support/exec_helpers.rb | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/gemstash/env.rb b/lib/gemstash/env.rb index 8a3acb2c..10b62473 100644 --- a/lib/gemstash/env.rb +++ b/lib/gemstash/env.rb @@ -2,6 +2,7 @@ require "dalli" require "fileutils" require "sequel" +require "uri" module Gemstash # Storage for application-wide variables and configuration. @@ -110,7 +111,7 @@ def db if RUBY_PLATFORM == "java" db = Sequel.connect("jdbc:sqlite:#{db_path}") else - db = Sequel.connect("sqlite://#{db_path}") + db = Sequel.connect("sqlite://#{URI.escape(db_path)}") end when "postgres" db = Sequel.connect(config[:db_url]) diff --git a/spec/support/exec_helpers.rb b/spec/support/exec_helpers.rb index 35fba0b5..e7f64a50 100644 --- a/spec/support/exec_helpers.rb +++ b/spec/support/exec_helpers.rb @@ -1,5 +1,6 @@ require "bundler/version" require "open3" +require "pathname" # Helpers for executing commands and asserting the results. module ExecHelpers @@ -15,7 +16,7 @@ def initialize(env, command, dir) @command = command @env = env @original_dir = dir - @dir = dir || File.expand_path(".") + @dir = dir || File.expand_path("../../..", __FILE__) @output, @status = Open3.capture2e(patched_env, @command, chdir: @dir) fix_jruby_output end @@ -70,7 +71,7 @@ def faster_jruby_env if command.start_with?("bundle") bundler_patch = File.expand_path("../jruby_bundler_monkeypatch.rb", __FILE__) - raise "Spaces in your path will cause build failures!" if bundler_patch.include?(" ") + bundler_patch = Pathname.new(bundler_patch).relative_path_from(Pathname.new(dir)) jruby_opts += " -r#{bundler_patch}" end From 9bbc7a9c617346c3375b80c2ba432172f06c3edd Mon Sep 17 00:00:00 2001 From: Mike Virata-Stone Date: Thu, 5 Nov 2015 00:33:33 -0800 Subject: [PATCH 5/7] Use 1 connection when using SQLite (fixes #42) --- lib/gemstash/env.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gemstash/env.rb b/lib/gemstash/env.rb index 10b62473..2eaa8373 100644 --- a/lib/gemstash/env.rb +++ b/lib/gemstash/env.rb @@ -109,9 +109,9 @@ def db db_path = base_file("gemstash.db") if RUBY_PLATFORM == "java" - db = Sequel.connect("jdbc:sqlite:#{db_path}") + db = Sequel.connect("jdbc:sqlite:#{db_path}", max_connections: 1) else - db = Sequel.connect("sqlite://#{URI.escape(db_path)}") + db = Sequel.connect("sqlite://#{URI.escape(db_path)}", max_connections: 1) end when "postgres" db = Sequel.connect(config[:db_url]) From 39d147ffc01d6f8d15873d1614e97500049b06f3 Mon Sep 17 00:00:00 2001 From: Mike Virata-Stone Date: Thu, 5 Nov 2015 17:44:17 -0800 Subject: [PATCH 6/7] Refactor exec in specs in JRuby to use the same JVM This requires significantly more memory to run, but results in much faster builds --- .travis.yml | 4 +- spec/integration_spec.rb | 23 +++++------ spec/spec_helper.rb | 1 + spec/support/exec_helpers.rb | 63 ++++++++++++++++++++++++++---- spec/support/in_process_exec.rb | 36 +++++++++++++++++ spec/support/jruby_binstubs/bundle | 25 ++++++++++++ spec/support/jruby_binstubs/gem | 10 +++++ 7 files changed, 142 insertions(+), 20 deletions(-) create mode 100644 spec/support/in_process_exec.rb create mode 100644 spec/support/jruby_binstubs/bundle create mode 100644 spec/support/jruby_binstubs/gem diff --git a/.travis.yml b/.travis.yml index f568488b..77eb0972 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,12 +2,14 @@ language: ruby matrix: allow_failures: - rvm: ruby-head + include: + - rvm: jruby-9.0.3.0 + env: JRUBY_OPTS="$JRUBY_OPTS -J-Xmx1536m -J-XX:MaxPermSize=192m" rvm: - 2.0 - 2.1 - 2.2 - ruby-head -- jruby-9.0.3.0 bundler_args: "--binstubs --jobs=3 --retry=3" before_install: gem install bundler -v 1.10.6 cache: bundler diff --git a/spec/integration_spec.rb b/spec/integration_spec.rb index 3b04e94a..16abce89 100644 --- a/spec/integration_spec.rb +++ b/spec/integration_spec.rb @@ -102,7 +102,7 @@ it "pushes valid gems to the server", :db_transaction => false do env = { "HOME" => env_dir } - expect(execute("gem push --key test --host '#{host}' '#{gem}'", env: env)).to exit_success + expect(execute("gem", ["push", "--key", "test", "--host", host, gem], env: env)).to exit_success expect(deps.fetch(%w(speaker))).to match_dependencies([speaker_deps]) expect(storage.resource("speaker-0.1.0").load.content).to eq(gem_contents) expect(http_client.get("gems/speaker-0.1.0")).to eq(gem_contents) @@ -118,7 +118,7 @@ it "removes valid gems from the server", :db_transaction => false do env = { "HOME" => env_dir, "RUBYGEMS_HOST" => host } - expect(execute("gem yank --key test '#{gem_name}' --version #{gem_version}", env: env)).to exit_success + expect(execute("gem", ["yank", "--key", "test", gem_name, "--version", gem_version], env: env)).to exit_success expect(deps.fetch(%w(speaker))).to match_dependencies([]) # It shouldn't actually delete the gem, to support unyank expect(storage.resource("speaker-0.1.0").load.content).to eq(gem_contents) @@ -137,7 +137,8 @@ it "adds valid gems back to the server", :db_transaction => false do env = { "HOME" => env_dir, "RUBYGEMS_HOST" => host } - expect(execute("gem yank --key test '#{gem_name}' --version #{gem_version} --undo", env: env)).to exit_success + expect(execute("gem", ["yank", "--key", "test", gem_name, "--version", gem_version, "--undo"], env: env)). + to exit_success expect(deps.fetch(%w(speaker))).to match_dependencies([speaker_deps]) expect(storage.resource("speaker-0.1.0").load.content).to eq(gem_contents) expect(http_client.get("gems/speaker-0.1.0")).to eq(gem_contents) @@ -163,27 +164,27 @@ shared_examples "a bundleable project" do it "successfully bundles" do expect(execute("bundle", dir: dir)).to exit_success - expect(execute("bundle exec speaker hi", dir: dir)). + expect(execute("bundle", %w(exec speaker hi), dir: dir)). to exit_success.and_output("Hello world, #{platform_message}\n") end it "can bundle with full index" do - expect(execute("bundle --full-index", dir: dir)).to exit_success - expect(execute("bundle exec speaker hi", dir: dir)). + expect(execute("bundle", %w(--full-index), dir: dir)).to exit_success + expect(execute("bundle", %w(exec speaker hi), dir: dir)). to exit_success.and_output("Hello world, #{platform_message}\n") end it "can bundle with prerelease versions" do env = { "SPEAKER_VERSION" => "= 0.2.0.pre" } expect(execute("bundle", dir: dir, env: env)).to exit_success - expect(execute("bundle exec speaker hi", dir: dir, env: env)). + expect(execute("bundle", %w(exec speaker hi), dir: dir, env: env)). to exit_success.and_output("Hello world, pre, #{platform_message}\n") end it "can bundle with prerelease versions with full index" do env = { "SPEAKER_VERSION" => "= 0.2.0.pre" } - expect(execute("bundle --full-index", dir: dir, env: env)).to exit_success - expect(execute("bundle exec speaker hi", dir: dir, env: env)). + expect(execute("bundle", %w(--full-index), dir: dir, env: env)).to exit_success + expect(execute("bundle", %w(exec speaker hi), dir: dir, env: env)). to exit_success.and_output("Hello world, pre, #{platform_message}\n") end end @@ -205,13 +206,13 @@ it "can successfully bundle twice" do expect(execute("bundle", dir: dir)).to exit_success - expect(execute("bundle exec speaker hi", dir: dir)). + expect(execute("bundle", %w(exec speaker hi), dir: dir)). to exit_success.and_output("Hello world, #{platform_message}\n") clean_bundle bundle expect(execute("bundle", dir: dir)).to exit_success - expect(execute("bundle exec speaker hi", dir: dir)). + expect(execute("bundle", %w(exec speaker hi), dir: dir)). to exit_success.and_output("Hello world, #{platform_message}\n") end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 1711ee82..55fe6116 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -8,6 +8,7 @@ require "support/env_helpers" require "support/exec_helpers" require "support/file_helpers" +require "support/in_process_exec" require "support/log_helpers" require "support/matchers" require "support/simple_server" diff --git a/spec/support/exec_helpers.rb b/spec/support/exec_helpers.rb index e7f64a50..0bae65c9 100644 --- a/spec/support/exec_helpers.rb +++ b/spec/support/exec_helpers.rb @@ -4,27 +4,48 @@ # Helpers for executing commands and asserting the results. module ExecHelpers - def execute(command, dir: nil, env: {}) - Result.new(env, command, dir) + def execute(command, args = [], dir: nil, env: {}) + if RUBY_PLATFORM == "java" + JRubyResult.new(env, command, args, dir) + else + Result.new(env, command, args, dir) + end end # Executes and stores the results for an external command. class Result - attr_reader :command, :dir, :output + attr_reader :command, :args, :dir, :output - def initialize(env, command, dir) + def initialize(env, command, args, dir) @command = command + @args = args @env = env @original_dir = dir @dir = dir || File.expand_path("../../..", __FILE__) - @output, @status = Open3.capture2e(patched_env, @command, chdir: @dir) + exec fix_jruby_output end + def exec + @output, @status = Open3.capture2e(patched_env, command, *args, chdir: dir) + end + def successful? @status.success? end + def display_command + display_args = args.map do |x| + if x =~ /\s/ + "'#{x}'" + else + x + end + end + + ([command] + display_args).join(" ") + end + def matches_output?(expected) return true unless expected @output == expected @@ -69,7 +90,7 @@ def faster_jruby_env return {} unless RUBY_PLATFORM == "java" jruby_opts = "--dev" - if command.start_with?("bundle") + if command == "bundle" bundler_patch = File.expand_path("../jruby_bundler_monkeypatch.rb", __FILE__) bundler_patch = Pathname.new(bundler_patch).relative_path_from(Pathname.new(dir)) jruby_opts += " -r#{bundler_patch}" @@ -78,6 +99,32 @@ def faster_jruby_env { "JRUBY_OPTS" => jruby_opts } end end + + # Executes and stores the results for an external command, but does it in + # process with a separate JRuby instance rather than a process. + class JRubyResult < Result + def exec + binstub_dir = File.expand_path("../jruby_binstubs", __FILE__) + binstub = File.join(binstub_dir, command) + + if File.exist?(binstub) + exec_in_process(binstub) + else + super + end + end + + def successful? + @process.status == 0 + end + + private + + def exec_in_process(binstub) + @process = InProcessExec.new(patched_env, dir, [binstub] + args) + @output = @process.output + end + end end RSpec::Matchers.define :exit_success do @@ -91,13 +138,13 @@ def faster_jruby_env failure_message do |actual| if actual.successful? - "expected '#{actual.command}' in '#{actual.dir}' to output: + "expected '#{actual.display_command}' in '#{actual.dir}' to output: #{@expected_output} but instead it output: #{actual.output}" else - "expected '#{actual.command}' in '#{actual.dir}' to exit with a success code, but it didn't. + "expected '#{actual.display_command}' in '#{actual.dir}' to exit with a success code, but it didn't. the command output was: #{actual.output}" end diff --git a/spec/support/in_process_exec.rb b/spec/support/in_process_exec.rb new file mode 100644 index 00000000..9afe9867 --- /dev/null +++ b/spec/support/in_process_exec.rb @@ -0,0 +1,36 @@ +# Run a JRuby program in process. +class InProcessExec + attr_reader :status, :output + + def initialize(env, dir, args) + raise "InProcessExec is only valid on JRuby!" unless RUBY_PLATFORM == "java" + @env = env + @dir = dir + @args = args + exec + end + +private + + def exec + prepare_streams + prepare_config + @status = org.jruby.Main.new(@config).run(@args.to_java(:String)).status + @output = @output_stream.to_string + end + + def prepare_streams + @input_stream = java.io.ByteArrayInputStream.new([].to_java(:byte)) + @output_stream = java.io.ByteArrayOutputStream.new + @output_print_stream = java.io.PrintStream.new(@output_stream) + end + + def prepare_config + @config = org.jruby.RubyInstanceConfig.new(JRuby.runtime.instance_config) + @config.environment = @env + @config.current_directory = @dir + @config.input = @input_stream + @config.output = @output_print_stream + @config.error = @output_print_stream + end +end diff --git a/spec/support/jruby_binstubs/bundle b/spec/support/jruby_binstubs/bundle new file mode 100644 index 00000000..1075bc33 --- /dev/null +++ b/spec/support/jruby_binstubs/bundle @@ -0,0 +1,25 @@ +require "rubygems" +gem "bundler" +require "bundler" +require "bundler/fetcher" +require_relative "../in_process_exec.rb" + +module Bundler + #:nodoc: + class Fetcher + # The Bundler user_agent uses SecureRandom, which causes the specs + # to run out of entropy and run a lot longer than they need to. + def user_agent + "gemstash spec" + end + end +end + +def Kernel.exec(*args) + args.pop if args.last.kind_of?(Hash) + process = InProcessExec.new(ENV, File.expand_path("."), args) + puts process.output + exit process.status +end + +load Gem.bin_path("bundler", "bundle") diff --git a/spec/support/jruby_binstubs/gem b/spec/support/jruby_binstubs/gem new file mode 100644 index 00000000..0634c6b5 --- /dev/null +++ b/spec/support/jruby_binstubs/gem @@ -0,0 +1,10 @@ +# Based on jgem +require "rubygems" +require "rubygems/gem_runner" +require "rubygems/exceptions" + +begin + Gem::GemRunner.new.run ARGV.clone +rescue Gem::SystemExitException => e + exit e.exit_code +end From 07c28b2fe53fda5e7019d14d256316fae0612249 Mon Sep 17 00:00:00 2001 From: Mike Virata-Stone Date: Thu, 5 Nov 2015 19:45:18 -0800 Subject: [PATCH 7/7] Cleanup some JRuby things that are no longer needed This and the previous commit fixes #46 --- spec/support/exec_helpers.rb | 29 +++-------------------- spec/support/jruby_bundler_monkeypatch.rb | 13 ---------- 2 files changed, 3 insertions(+), 39 deletions(-) delete mode 100644 spec/support/jruby_bundler_monkeypatch.rb diff --git a/spec/support/exec_helpers.rb b/spec/support/exec_helpers.rb index 0bae65c9..6ddbcb81 100644 --- a/spec/support/exec_helpers.rb +++ b/spec/support/exec_helpers.rb @@ -57,16 +57,10 @@ def fix_jruby_output return unless RUBY_PLATFORM == "java" # Travis builds or runs JRuby in a way that outputs the following warning for some reason @output.gsub!(/^.*warning: unknown property jruby.cext.enabled\n/, "") - - if Gem::Requirement.new(">= 1.11.0").satisfied_by?(Gem::Version.new(Bundler::VERSION)) - raise "Please remove ExecHelpers#fix_jruby_output if the warning doesn't occur anymore" - end - - @output.gsub!(/^.*warning: unsupported exec option: close_others\n/, "") end def patched_env - @patched_env ||= faster_jruby_env.merge(clear_bundler_env).merge(clear_ruby_env).merge(@env) + @patched_env ||= clear_bundler_env.merge(clear_ruby_env).merge(@env) end def clear_ruby_env @@ -85,19 +79,6 @@ def clear_bundler_env { "BUNDLE_GEMFILE" => bundle_gemfile } end - - def faster_jruby_env - return {} unless RUBY_PLATFORM == "java" - jruby_opts = "--dev" - - if command == "bundle" - bundler_patch = File.expand_path("../jruby_bundler_monkeypatch.rb", __FILE__) - bundler_patch = Pathname.new(bundler_patch).relative_path_from(Pathname.new(dir)) - jruby_opts += " -r#{bundler_patch}" - end - - { "JRUBY_OPTS" => jruby_opts } - end end # Executes and stores the results for an external command, but does it in @@ -106,12 +87,8 @@ class JRubyResult < Result def exec binstub_dir = File.expand_path("../jruby_binstubs", __FILE__) binstub = File.join(binstub_dir, command) - - if File.exist?(binstub) - exec_in_process(binstub) - else - super - end + raise "Missing binstub for #{command}" unless File.exist?(binstub) + exec_in_process(binstub) end def successful? diff --git a/spec/support/jruby_bundler_monkeypatch.rb b/spec/support/jruby_bundler_monkeypatch.rb deleted file mode 100644 index 9c0e84df..00000000 --- a/spec/support/jruby_bundler_monkeypatch.rb +++ /dev/null @@ -1,13 +0,0 @@ -require "bundler" -require "bundler/fetcher" - -module Bundler - #:nodoc: - class Fetcher - # The Bundler user_agent uses SecureRandom, which causes the specs - # to run out of entropy and run a lot longer than they need to. - def user_agent - "gemstash spec" - end - end -end