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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rubocop.yml +158 -0
  4. data/Gemfile +4 -0
  5. data/README.md +35 -0
  6. data/Rakefile +4 -0
  7. data/bin/console +15 -0
  8. data/bin/setup +8 -0
  9. data/hanami-devtools.gemspec +42 -0
  10. data/lib/hanami/devtools/integration/bundler.rb +174 -0
  11. data/lib/hanami/devtools/integration/capybara.rb +20 -0
  12. data/lib/hanami/devtools/integration/cli.rb +57 -0
  13. data/lib/hanami/devtools/integration/coverage.rb +55 -0
  14. data/lib/hanami/devtools/integration/dns.rb +26 -0
  15. data/lib/hanami/devtools/integration/env.rb +97 -0
  16. data/lib/hanami/devtools/integration/files.rb +108 -0
  17. data/lib/hanami/devtools/integration/gemfile.rb +38 -0
  18. data/lib/hanami/devtools/integration/hanami_commands.rb +169 -0
  19. data/lib/hanami/devtools/integration/platform/engine.rb +32 -0
  20. data/lib/hanami/devtools/integration/platform/matcher.rb +79 -0
  21. data/lib/hanami/devtools/integration/platform/os.rb +21 -0
  22. data/lib/hanami/devtools/integration/platform.rb +22 -0
  23. data/lib/hanami/devtools/integration/project_without_hanami_model.rb +26 -0
  24. data/lib/hanami/devtools/integration/rack_test.rb +87 -0
  25. data/lib/hanami/devtools/integration/random_port.rb +46 -0
  26. data/lib/hanami/devtools/integration/retry.rb +36 -0
  27. data/lib/hanami/devtools/integration/silently.rb +35 -0
  28. data/lib/hanami/devtools/integration/with_clean_env_project.rb +29 -0
  29. data/lib/hanami/devtools/integration/with_directory.rb +30 -0
  30. data/lib/hanami/devtools/integration/with_project.rb +77 -0
  31. data/lib/hanami/devtools/integration/with_system_tmp_directory.rb +24 -0
  32. data/lib/hanami/devtools/integration/with_tmp_directory.rb +48 -0
  33. data/lib/hanami/devtools/integration/within_project_directory.rb +37 -0
  34. data/lib/hanami/devtools/integration.rb +4 -0
  35. data/lib/hanami/devtools/rake_helper.rb +31 -0
  36. data/lib/hanami/devtools/rake_tasks.rb +4 -0
  37. data/lib/hanami/devtools/unit/support/coverage.rb +10 -0
  38. data/lib/hanami/devtools/unit/support/silence_deprecations.rb +12 -0
  39. data/lib/hanami/devtools/unit.rb +4 -0
  40. data/lib/hanami/devtools/version.rb +7 -0
  41. data/lib/hanami/devtools.rb +8 -0
  42. data/script/setup +36 -0
  43. 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,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/utils"
4
+ Hanami::Utils.require!("#{__dir__}/integration")
@@ -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
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/devtools/rake_helper"
4
+ Hanami::Devtools::RakeHelper.install_tasks
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ if ENV["CI"]
4
+ require "simplecov"
5
+
6
+ SimpleCov.command_name ENV["SIMPLECOV_COMMAND_NAME"] || "spec:unit"
7
+ SimpleCov.start do
8
+ add_filter(/spec/)
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rspec"
4
+ require "hanami/utils/io"
5
+
6
+ RSpec.configure do |config|
7
+ config.around(:example, silence_deprecations: true) do |example|
8
+ Hanami::Utils::IO.silence_warnings do
9
+ example.run
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/utils"
4
+ Hanami::Utils.require!("#{__dir__}/unit")
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module Devtools
5
+ VERSION = "2023.02.16"
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ # Hanami devtools
5
+ module Devtools
6
+ require "hanami/devtools/version"
7
+ end
8
+ 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