hanami-devtools 2023.02.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rubocop.yml +158 -0
- data/Gemfile +4 -0
- data/README.md +35 -0
- data/Rakefile +4 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/hanami-devtools.gemspec +42 -0
- data/lib/hanami/devtools/integration/bundler.rb +174 -0
- data/lib/hanami/devtools/integration/capybara.rb +20 -0
- data/lib/hanami/devtools/integration/cli.rb +57 -0
- data/lib/hanami/devtools/integration/coverage.rb +55 -0
- data/lib/hanami/devtools/integration/dns.rb +26 -0
- data/lib/hanami/devtools/integration/env.rb +97 -0
- data/lib/hanami/devtools/integration/files.rb +108 -0
- data/lib/hanami/devtools/integration/gemfile.rb +38 -0
- data/lib/hanami/devtools/integration/hanami_commands.rb +169 -0
- data/lib/hanami/devtools/integration/platform/engine.rb +32 -0
- data/lib/hanami/devtools/integration/platform/matcher.rb +79 -0
- data/lib/hanami/devtools/integration/platform/os.rb +21 -0
- data/lib/hanami/devtools/integration/platform.rb +22 -0
- data/lib/hanami/devtools/integration/project_without_hanami_model.rb +26 -0
- data/lib/hanami/devtools/integration/rack_test.rb +87 -0
- data/lib/hanami/devtools/integration/random_port.rb +46 -0
- data/lib/hanami/devtools/integration/retry.rb +36 -0
- data/lib/hanami/devtools/integration/silently.rb +35 -0
- data/lib/hanami/devtools/integration/with_clean_env_project.rb +29 -0
- data/lib/hanami/devtools/integration/with_directory.rb +30 -0
- data/lib/hanami/devtools/integration/with_project.rb +77 -0
- data/lib/hanami/devtools/integration/with_system_tmp_directory.rb +24 -0
- data/lib/hanami/devtools/integration/with_tmp_directory.rb +48 -0
- data/lib/hanami/devtools/integration/within_project_directory.rb +37 -0
- data/lib/hanami/devtools/integration.rb +4 -0
- data/lib/hanami/devtools/rake_helper.rb +31 -0
- data/lib/hanami/devtools/rake_tasks.rb +4 -0
- data/lib/hanami/devtools/unit/support/coverage.rb +10 -0
- data/lib/hanami/devtools/unit/support/silence_deprecations.rb +12 -0
- data/lib/hanami/devtools/unit.rb +4 -0
- data/lib/hanami/devtools/version.rb +7 -0
- data/lib/hanami/devtools.rb +8 -0
- data/script/setup +36 -0
- metadata +266 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "socket"
|
|
4
|
+
require "timeout"
|
|
5
|
+
|
|
6
|
+
module RSpec
|
|
7
|
+
module Support
|
|
8
|
+
# Returns a random, non-sudo, free port
|
|
9
|
+
#
|
|
10
|
+
# @since 0.2.0
|
|
11
|
+
module RandomPort
|
|
12
|
+
HOST = "localhost"
|
|
13
|
+
PORT_RANGE = 1024..65_535
|
|
14
|
+
TIMEOUT = 1 # second
|
|
15
|
+
|
|
16
|
+
def self.call
|
|
17
|
+
result = -1
|
|
18
|
+
|
|
19
|
+
loop do
|
|
20
|
+
result = Kernel.rand(PORT_RANGE)
|
|
21
|
+
break unless open?(result)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
result
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.open?(port) # rubocop:disable Metrics/MethodLength
|
|
28
|
+
Timeout.timeout(TIMEOUT) do
|
|
29
|
+
begin
|
|
30
|
+
s = TCPSocket.new(HOST, port)
|
|
31
|
+
s.close
|
|
32
|
+
return true
|
|
33
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::EADDRNOTAVAIL
|
|
34
|
+
return false
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
false
|
|
39
|
+
rescue Timeout::Error
|
|
40
|
+
false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private_class_method :open?
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RSpec
|
|
4
|
+
module Support
|
|
5
|
+
# Implement retry logic.
|
|
6
|
+
#
|
|
7
|
+
# It may happen that the setup of a test is slower then expected.
|
|
8
|
+
# Instead of let it to blow up, we can retry after a few seconds.
|
|
9
|
+
#
|
|
10
|
+
# @since 0.2.0
|
|
11
|
+
module Retry
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def retry_exec(exception) # rubocop:disable Metrics/MethodLength
|
|
15
|
+
attempts = 1
|
|
16
|
+
max_retry_attempts = Platform.match do
|
|
17
|
+
engine(:ruby) { 10 }
|
|
18
|
+
engine(:jruby) { 20 }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
begin
|
|
22
|
+
sleep 1
|
|
23
|
+
yield
|
|
24
|
+
rescue exception
|
|
25
|
+
raise if attempts > max_retry_attempts
|
|
26
|
+
attempts += 1
|
|
27
|
+
retry
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
RSpec.configure do |config|
|
|
35
|
+
config.include RSpec::Support::Retry, type: :integration
|
|
36
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tempfile"
|
|
4
|
+
|
|
5
|
+
module RSpec
|
|
6
|
+
# RSpec support utilities
|
|
7
|
+
#
|
|
8
|
+
# @since 0.2.0
|
|
9
|
+
module Support
|
|
10
|
+
def self.silently(cmd)
|
|
11
|
+
out = Tempfile.new("hanami-out")
|
|
12
|
+
result = system(cmd, out: out.path)
|
|
13
|
+
|
|
14
|
+
return if result
|
|
15
|
+
|
|
16
|
+
out.rewind
|
|
17
|
+
fail "#{cmd} failed:\n#{out.read}" # rubocop:disable Style/SignalException
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Don't print unnecessary output during tests
|
|
21
|
+
#
|
|
22
|
+
# @since 0.2.0
|
|
23
|
+
module Silently
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def silently(cmd)
|
|
27
|
+
Support.silently(cmd)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
RSpec.configure do |config|
|
|
34
|
+
config.include RSpec::Support::Silently, type: :integration
|
|
35
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/devtools/integration/with_project"
|
|
4
|
+
require "hanami/devtools/integration/bundler"
|
|
5
|
+
|
|
6
|
+
module RSpec
|
|
7
|
+
module Support
|
|
8
|
+
# Generate a Hanami project with a Bundler clean environment.
|
|
9
|
+
#
|
|
10
|
+
# @since 0.2.0
|
|
11
|
+
#
|
|
12
|
+
# @see http://bundler.io/man/bundle-exec.1.html#Shelling-out
|
|
13
|
+
module WithCleanEnvProject
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def with_clean_env_project(project = "bookshelf", args = {})
|
|
17
|
+
RSpec::Support::Bundler.with_clean_env do
|
|
18
|
+
with_project(project, args) do
|
|
19
|
+
yield
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
RSpec.configure do |config|
|
|
28
|
+
config.include RSpec::Support::WithCleanEnvProject, type: :integration
|
|
29
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pathname"
|
|
4
|
+
|
|
5
|
+
module RSpec
|
|
6
|
+
module Support
|
|
7
|
+
# Execute the given code inside a specified directory.
|
|
8
|
+
#
|
|
9
|
+
# NOTE: this changes the current `Dir.pwd`
|
|
10
|
+
#
|
|
11
|
+
# @since 0.2.0
|
|
12
|
+
module WithDirectory
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def with_directory(directory)
|
|
16
|
+
current = Dir.pwd
|
|
17
|
+
target = Pathname.new(Dir.pwd).join(directory)
|
|
18
|
+
|
|
19
|
+
Dir.chdir(target)
|
|
20
|
+
yield
|
|
21
|
+
ensure
|
|
22
|
+
Dir.chdir(current)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
RSpec.configure do |config|
|
|
29
|
+
config.include RSpec::Support::WithDirectory
|
|
30
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/devtools/integration/silently"
|
|
4
|
+
require "hanami/devtools/integration/bundler"
|
|
5
|
+
require "hanami/devtools/integration/with_tmp_directory"
|
|
6
|
+
require "hanami/devtools/integration/within_project_directory"
|
|
7
|
+
|
|
8
|
+
module RSpec
|
|
9
|
+
module Support
|
|
10
|
+
# Generates a Hanami project
|
|
11
|
+
#
|
|
12
|
+
# @since 0.2.0
|
|
13
|
+
module WithProject
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
KNOWN_ARGUMENTS = %i[database template test].freeze
|
|
17
|
+
|
|
18
|
+
def with_project(project = "bookshelf", args = {}) # rubocop:disable Metrics/MethodLength
|
|
19
|
+
with_tmp_directory do
|
|
20
|
+
create_project(project, args)
|
|
21
|
+
|
|
22
|
+
within_project_directory(project) do
|
|
23
|
+
begin
|
|
24
|
+
setup_gemfile(gems: gem_dependencies(args), exclude_gems: args.fetch(:exclude_gems, []))
|
|
25
|
+
bundle_install
|
|
26
|
+
yield
|
|
27
|
+
ensure
|
|
28
|
+
restore_gemfile
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def create_project(project, args)
|
|
35
|
+
silently "hanami new #{project}#{_create_project_args(args)}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def gem_dependencies(args) # rubocop:disable Metrics/MethodLength
|
|
39
|
+
result = args.fetch(:gems, [])
|
|
40
|
+
|
|
41
|
+
case args.fetch(:console, nil)
|
|
42
|
+
when :pry
|
|
43
|
+
result << "pry"
|
|
44
|
+
when :ripl
|
|
45
|
+
result << "ripl"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
case args.fetch(:server, nil)
|
|
49
|
+
when :puma
|
|
50
|
+
result << "puma"
|
|
51
|
+
when :unicorn
|
|
52
|
+
result << "unicorn"
|
|
53
|
+
when :thin
|
|
54
|
+
result << "thin"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
result
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def _create_project_args(args)
|
|
61
|
+
return if args.empty?
|
|
62
|
+
|
|
63
|
+
flags = args.dup.keep_if { |k, _| KNOWN_ARGUMENTS.include?(k) }
|
|
64
|
+
|
|
65
|
+
result = flags.map do |arg, value|
|
|
66
|
+
"--#{arg}=#{value}"
|
|
67
|
+
end.join(" ")
|
|
68
|
+
|
|
69
|
+
" #{result}"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
RSpec.configure do |config|
|
|
76
|
+
config.include RSpec::Support::WithProject, type: :integration
|
|
77
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/devtools/integration/with_tmp_directory"
|
|
4
|
+
|
|
5
|
+
module RSpec
|
|
6
|
+
module Support
|
|
7
|
+
# Execute the given code inside the system temporary directory (aka `/tmp`).
|
|
8
|
+
#
|
|
9
|
+
# NOTE: this changes the current `Dir.pwd`
|
|
10
|
+
#
|
|
11
|
+
# @since 0.2.0
|
|
12
|
+
module WithSystemTmpDirectory
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def with_system_tmp_directory(&blk)
|
|
16
|
+
with_tmp_directory(Dir.mktmpdir, &blk)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
RSpec.configure do |config|
|
|
23
|
+
config.include RSpec::Support::WithSystemTmpDirectory, type: :integration
|
|
24
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "hanami/utils/files"
|
|
5
|
+
require "hanami/devtools/integration/with_directory"
|
|
6
|
+
|
|
7
|
+
module RSpec
|
|
8
|
+
module Support
|
|
9
|
+
# Execute the given code inside a temporary directory.
|
|
10
|
+
#
|
|
11
|
+
# NOTE: this changes the current `Dir.pwd`
|
|
12
|
+
#
|
|
13
|
+
# @since 0.2.0
|
|
14
|
+
module WithTmpDirectory
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def with_tmp_directory(dir = Pathname.new("tmp").join("aruba"))
|
|
18
|
+
delete_tmp_directory(dir)
|
|
19
|
+
create_tmp_directory(dir)
|
|
20
|
+
|
|
21
|
+
with_directory(dir) do
|
|
22
|
+
yield
|
|
23
|
+
end
|
|
24
|
+
ensure
|
|
25
|
+
delete_tmp_directory(dir)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def create_tmp_directory(dir)
|
|
29
|
+
FileUtils.mkdir_p(dir)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def delete_tmp_directory(dir)
|
|
33
|
+
directory = case dir
|
|
34
|
+
when Pathname
|
|
35
|
+
dir
|
|
36
|
+
when String
|
|
37
|
+
Pathname.new(dir)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
Hanami::Utils::Files.delete_directory(directory) if directory.exist?
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
RSpec.configure do |config|
|
|
47
|
+
config.include RSpec::Support::WithTmpDirectory, type: :integration
|
|
48
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pathname"
|
|
4
|
+
require "hanami/devtools/integration/with_directory"
|
|
5
|
+
require "hanami/devtools/integration/env"
|
|
6
|
+
|
|
7
|
+
module RSpec
|
|
8
|
+
module Support
|
|
9
|
+
# Execute the given code inside the Hanami project directory (at the root).
|
|
10
|
+
#
|
|
11
|
+
# NOTE: this changes the current `Dir.pwd`
|
|
12
|
+
#
|
|
13
|
+
# @since 0.2.0
|
|
14
|
+
module WithinProjectDirectory
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def within_project_directory(project)
|
|
18
|
+
cd(project.to_s) do
|
|
19
|
+
# Aruba resets ENV and its API to set new env vars is broken.
|
|
20
|
+
#
|
|
21
|
+
# We need to manually setup the following env vars:
|
|
22
|
+
#
|
|
23
|
+
# ENV["PATH"] is required by Capybara's selenium/poltergeist drivers
|
|
24
|
+
ENV["PATH"] = RSpec::Support::Env.fetch_from_original("PATH")
|
|
25
|
+
# Bundler on CI can't find HOME and it fails to run Hanami commands
|
|
26
|
+
ENV["HOME"] = RSpec::Support::Env.fetch_from_original("HOME")
|
|
27
|
+
|
|
28
|
+
yield
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
RSpec.configure do |config|
|
|
36
|
+
config.include RSpec::Support::WithinProjectDirectory
|
|
37
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rake"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
module Devtools
|
|
7
|
+
# Distribute Rake tasks
|
|
8
|
+
class RakeHelper
|
|
9
|
+
include Rake::DSL
|
|
10
|
+
|
|
11
|
+
def self.install_tasks
|
|
12
|
+
new.install
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def install # rubocop:disable Metrics/MethodLength
|
|
16
|
+
namespace :codecov do
|
|
17
|
+
desc "Uploads the latest simplecov result set to codecov.io"
|
|
18
|
+
task :upload do
|
|
19
|
+
if ENV["CI"]
|
|
20
|
+
require "simplecov"
|
|
21
|
+
require "codecov"
|
|
22
|
+
|
|
23
|
+
formatter = SimpleCov::Formatter::Codecov.new
|
|
24
|
+
formatter.format(SimpleCov::ResultMerger.merged_result)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/script/setup
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
IFS=$'\n\t'
|
|
4
|
+
|
|
5
|
+
banner() {
|
|
6
|
+
echo "Using hanami-devtools at: $(git rev-parse --short HEAD)"
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
install_hanami() {
|
|
10
|
+
hanami_gems=($(bundle show | grep hanami))
|
|
11
|
+
|
|
12
|
+
for bundler_gem_info in "${hanami_gems[@]}"
|
|
13
|
+
do
|
|
14
|
+
local gem_name=$(echo $bundler_gem_info | cut -d' ' -f 4)
|
|
15
|
+
local gem_directory=$(bundle show $gem_name)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
pushd $gem_directory > /dev/null
|
|
19
|
+
find . -name \*.gem -delete
|
|
20
|
+
gem build $gem_name.gemspec > /dev/null
|
|
21
|
+
|
|
22
|
+
local pkg=($gem_name-*.gem)
|
|
23
|
+
gem install --force $pkg > /dev/null
|
|
24
|
+
rm $pkg
|
|
25
|
+
|
|
26
|
+
echo Installed $pkg from $gem_directory
|
|
27
|
+
popd > /dev/null
|
|
28
|
+
done
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
main() {
|
|
32
|
+
banner &&
|
|
33
|
+
install_hanami
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
main
|