hanami-cli 2.1.0.rc2 → 2.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aa0dbea6bc97ed3a7d08b703e7fd9091def9a8326196d36c071fb8e3e9be654b
4
- data.tar.gz: dc27646ce5d395932bd2fbddef1678cb5445959494270b8d5f149462a8648e90
3
+ metadata.gz: f0885d4f1e10f7f8bd8691c04524be0f6353de0b0a7ce19c2ecf42ae31c8f8cd
4
+ data.tar.gz: d6c27d7571896e9f93d2ab41206e50f922fdfecc892ccf3d2328f9123c6d2348
5
5
  SHA512:
6
- metadata.gz: ac1ab34a920c7fea44106a75c8c7e8d1bb0e475ff1e48ce2ef9b87912e9da6c83aa847ddf56661db13a01551fd58e098245b6d8ac277ca7143c69681d97d5ece
7
- data.tar.gz: 706d1bb79523ba8a7efd9281267de12a0f275be0b60374c619b132371e0339335e6a4112b7a53ed5951e57293799aea1b4a89ac5bf6f9ee718daec4e1c44ee45
6
+ metadata.gz: 74180e374ac3836550e622af10dc998bc5356d3a745c2b0731ef2f03e2bc06f639ab5c375d7d53e7dc67fe40672eb80c38b77bb5cc6111d52d9a20da415de222
7
+ data.tar.gz: 94596b8dd6f53f2dc6d53f37bfcec37b5002324ef85715c9791b94ebb0fa198884c8acf7d339752e9bb11b0da9f8fbddb4fa5f89ec62fb49944af8cc8732087d
@@ -24,6 +24,7 @@ jobs:
24
24
  fail-fast: false
25
25
  matrix:
26
26
  ruby:
27
+ - "3.3"
27
28
  - "3.2"
28
29
  - "3.1"
29
30
  - "3.0"
data/.rubocop.yml CHANGED
@@ -13,6 +13,8 @@ Lint/DuplicateBranch:
13
13
  Lint/EmptyFile:
14
14
  Exclude:
15
15
  - 'spec/fixtures/**/*.rb'
16
+ Lint/NonLocalExitFromIterator:
17
+ Enabled: false
16
18
  Naming/HeredocDelimiterNaming:
17
19
  Enabled: false
18
20
  Naming/MethodParameterName:
data/CHANGELOG.md CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  Hanami Command Line Interface
4
4
 
5
+ ## v2.1.0 - 2024-02-27
6
+
7
+ ### Changed
8
+
9
+ - [Tim Riley] Underscore slice names in `public/assets/` to avoid naming conflicts with nested asset entry points. In this arrangement, an "admin" slice will have its assets compiled into `public/assets/_admin/`.
10
+
11
+ ## v2.1.0.rc3 - 2024-02-16
12
+
13
+ ### Changed
14
+
15
+ - [Tim Riley] For `hanami assets` commands, run a separate assets compilation process per slice (in parallel).
16
+ - [Tim Riley] Generate compiled assets into separate folders per slice, each with its own `assets.js` manifest file: `public/assets/` for the app, and `public/assets/[slice_name]/` for each slice.
17
+ - [Tim Riley] For `hanami assets` commands, directly detect and invoke the `config/assets.js` files. Look for this file within each slice, and fall back to the app-level file.
18
+ - [Tim Riley] Do not generate `"scripts": {"assets": "..."}` section in new app's `package.json`.
19
+ - [Tim Riley] Subclasses of `Hanami::CLI::Command` receive default args to their `#initialize` methods, and do not need to re-declare default args themselves.
20
+ - [Philip Arndt] Alphabetically sort hanami gems in the new app `Gemfile`.
21
+
22
+ ### Fixed
23
+
24
+ - [Nishiki (錦華)] Strip invalid characters from module name when generating new app.
25
+
5
26
  ## v2.1.0.rc2 - 2023-11-08
6
27
 
7
28
  ### Added
data/Gemfile CHANGED
@@ -19,6 +19,8 @@ gem "dry-files", github: "dry-rb/dry-files", branch: "main"
19
19
 
20
20
  gem "rack"
21
21
 
22
+ gem "hanami-devtools", github: "hanami/devtools", branch: "main"
23
+
22
24
  group :test do
23
25
  gem "pry"
24
26
  end
@@ -13,18 +13,40 @@ module Hanami
13
13
  class Command < Dry::CLI::Command
14
14
  # Returns a new command.
15
15
  #
16
- # This method does not need to be called directly when creating comments for the CLI. Commands
16
+ # Provides default values so they can be available to any subclasses defining their own
17
+ # {#initialize} methods.
18
+ #
19
+ # @see #initialize
20
+ #
21
+ # @since 2.1.0
22
+ # @api public
23
+ def self.new(
24
+ out: $stdout,
25
+ err: $stderr,
26
+ fs: Hanami::CLI::Files.new,
27
+ inflector: Dry::Inflector.new,
28
+ **opts
29
+ )
30
+ super(out: out, err: err, fs: fs, inflector: inflector, **opts)
31
+ end
32
+
33
+ # Returns a new command.
34
+ #
35
+ # This method does not need to be called directly when creating commands for the CLI. Commands
17
36
  # are registered as classes, and the CLI framework will initialize the command when needed.
18
- # This means that all parameters for `#initialize` should also be given default arguments.
37
+ # This means that all parameters for `#initialize` should also be given default arguments. See
38
+ # {.new} for the standard default arguments for all commands.
19
39
  #
20
40
  # @param out [IO] I/O stream for standard command output
21
41
  # @param err [IO] I/O stream for comment errror output
22
42
  # @param fs [Hanami::CLI::Files] object for managing file system interactions
23
43
  # @param inflector [Dry::Inflector] inflector for any command-level inflections
24
44
  #
45
+ # @see .new
46
+ #
25
47
  # @since 2.0.0
26
48
  # @api public
27
- def initialize(out: $stdout, err: $stderr, fs: Hanami::CLI::Files.new, inflector: Dry::Inflector.new)
49
+ def initialize(out:, err:, fs:, inflector:)
28
50
  super()
29
51
  @out = out
30
52
  @err = err
@@ -2,28 +2,71 @@
2
2
 
3
3
  require "shellwords"
4
4
  require_relative "../command"
5
- require_relative "../../../system_call"
5
+ require_relative "../../../interactive_system_call"
6
6
 
7
7
  module Hanami
8
8
  module CLI
9
9
  module Commands
10
10
  module App
11
11
  module Assets
12
+ # Base class for assets commands.
13
+ #
14
+ # Finds slices with assets present (anything in an `assets/` dir), then forks a child
15
+ # process for each slice to run the assets command (`config/assets.js`) for the slice.
16
+ #
17
+ # Prefers the slice's own `config/assets.js` if present, otherwise falls back to the
18
+ # app-level file.
19
+ #
20
+ # Passes `--path` and `--dest` arguments to this command to compile assets for the given
21
+ # slice only and save them into a dedicated directory (`public/assets/` for the app,
22
+ # `public/[slice_name]/` for slices).
23
+ #
24
+ # @see Watch
25
+ # @see Compile
26
+ #
12
27
  # @since 2.1.0
13
28
  # @api private
14
29
  class Command < App::Command
15
- def initialize(config: app.config.assets, system_call: SystemCall.new, **)
16
- super()
17
- @system_call = system_call
30
+ # @since 2.1.0
31
+ # @api private
32
+ def initialize(
33
+ out:, err:,
34
+ config: app.config.assets,
35
+ system_call: InteractiveSystemCall.new(out: out, err: err, exit_after: false),
36
+ **opts
37
+ )
38
+ super(out: out, err: err, **opts)
39
+
18
40
  @config = config
41
+ @system_call = system_call
19
42
  end
20
43
 
21
44
  # @since 2.1.0
22
45
  # @api private
23
46
  def call(**)
24
- cmd, *args = cmd_with_args
47
+ slices = slices_with_assets
48
+
49
+ if slices.empty?
50
+ out.puts "No assets found."
51
+ return
52
+ end
53
+
54
+ slices.each do |slice|
55
+ unless assets_config(slice)
56
+ out.puts "No assets config found for #{slice}. Please create a config/assets.js."
57
+ return
58
+ end
59
+ end
60
+
61
+ pids = slices_with_assets.map { |slice| fork_child_assets_command(slice) }
25
62
 
26
- system_call.call(cmd, *args)
63
+ Signal.trap("INT") do
64
+ pids.each do |pid|
65
+ Process.kill(sig, pid)
66
+ end
67
+ end
68
+
69
+ Process.waitall
27
70
  end
28
71
 
29
72
  private
@@ -38,8 +81,65 @@ module Hanami
38
81
 
39
82
  # @since 2.1.0
40
83
  # @api private
41
- def cmd_with_args
42
- [config.package_manager_run_command, "assets"]
84
+ def fork_child_assets_command(slice)
85
+ Process.fork do
86
+ cmd, *args = assets_command(slice)
87
+ system_call.call(cmd, *args, out_prefix: "[#{slice.slice_name}] ")
88
+ rescue Interrupt
89
+ # When this has been interrupted (by the Signal.trap handler in #call), catch the
90
+ # interrupt and exit cleanly, without showing the default full backtrace.
91
+ end
92
+ end
93
+
94
+ # @since 2.1.0
95
+ # @api private
96
+ def assets_command(slice)
97
+ cmd = [config.node_command, assets_config(slice).to_s, "--"]
98
+
99
+ if slice.eql?(slice.app)
100
+ cmd << "--path=app"
101
+ cmd << "--dest=public/assets"
102
+ else
103
+ cmd << "--path=#{slice.root.relative_path_from(slice.app.root)}"
104
+ cmd << "--dest=public/assets/#{Hanami::Assets.public_assets_dir(slice)}"
105
+ end
106
+
107
+ cmd
108
+ end
109
+
110
+ # @since 2.1.0
111
+ # @api private
112
+ def slices_with_assets
113
+ slices = app.slices.with_nested + [app]
114
+ slices.select { |slice| slice_assets?(slice) }
115
+ end
116
+
117
+ # @since 2.1.0
118
+ # @api private
119
+ def slice_assets?(slice)
120
+ assets_path =
121
+ if slice.app.eql?(slice)
122
+ slice.root.join("app", "assets")
123
+ else
124
+ slice.root.join("assets")
125
+ end
126
+
127
+ assets_path.directory?
128
+ end
129
+
130
+ # Returns the path to the assets config (`config/assets.js`) for the given slice.
131
+ #
132
+ # Prefers a config file local to the slice, otherwise falls back to app-level config.
133
+ # Returns nil if no config can be found.
134
+ #
135
+ # @since 2.1.0
136
+ # @api private
137
+ def assets_config(slice)
138
+ config = slice.root.join("config", "assets.js")
139
+ return config if config.exist?
140
+
141
+ config = slice.app.root.join("config", "assets.js")
142
+ config if config.exist?
43
143
  end
44
144
 
45
145
  # @since 2.1.0
@@ -7,22 +7,25 @@ module Hanami
7
7
  module Commands
8
8
  module App
9
9
  module Assets
10
+ # Compiles assets for each slice.
11
+ #
10
12
  # @since 2.1.0
11
13
  # @api private
12
14
  class Compile < Assets::Command
13
15
  desc "Compile assets for deployments"
14
16
 
17
+ private
18
+
15
19
  # @since 2.1.0
16
20
  # @api private
17
- def cmd_with_args
18
- result = super
21
+ def assets_command(slice)
22
+ cmd = super
19
23
 
20
24
  if config.subresource_integrity.any?
21
- result << "--"
22
- result << "--sri=#{escape(config.subresource_integrity.join(','))}"
25
+ cmd << "--sri=#{escape(config.subresource_integrity.join(','))}"
23
26
  end
24
27
 
25
- result
28
+ cmd
26
29
  end
27
30
  end
28
31
  end
@@ -1,28 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "command"
4
- require_relative "../../../interactive_system_call"
5
4
 
6
5
  module Hanami
7
6
  module CLI
8
7
  module Commands
9
8
  module App
10
9
  module Assets
10
+ # Watches for asset changes within each slice.
11
+ #
11
12
  # @since 2.1.0
12
13
  # @api private
13
14
  class Watch < Assets::Command
14
15
  desc "Start assets watch mode"
15
16
 
16
- def initialize(config: app.config.assets, system_call: InteractiveSystemCall.new, **)
17
- super(config: config, system_call: system_call)
18
- end
19
-
20
17
  private
21
18
 
22
19
  # @since 2.1.0
23
20
  # @api private
24
- def cmd_with_args
25
- super + ["--", "--watch"]
21
+ def assets_command(slice)
22
+ super + ["--watch"]
26
23
  end
27
24
  end
28
25
  end
@@ -15,23 +15,28 @@ module Hanami
15
15
 
16
16
  # @since 2.1.0
17
17
  # @api private
18
- def initialize(interactive_system_call: InteractiveSystemCall.new, **)
19
- @interactive_system_call = interactive_system_call
20
- super()
18
+ def initialize(
19
+ out:, err:,
20
+ system_call: InteractiveSystemCall.new(out: out, err: err),
21
+ **opts
22
+ )
23
+ super(out: out, err: err, **opts)
24
+
25
+ @system_call = system_call
21
26
  end
22
27
 
23
28
  # @since 2.1.0
24
29
  # @api private
25
30
  def call(**)
26
31
  bin, args = executable
27
- interactive_system_call.call(bin, *args)
32
+ system_call.call(bin, *args)
28
33
  end
29
34
 
30
35
  private
31
36
 
32
37
  # @since 2.1.0
33
38
  # @api private
34
- attr_reader :interactive_system_call
39
+ attr_reader :system_call
35
40
 
36
41
  # @since 2.1.0
37
42
  # @api private
@@ -61,12 +61,16 @@ module Hanami
61
61
 
62
62
  # @since 2.0.0
63
63
  # @api private
64
- def initialize(fs: Hanami::CLI::Files.new, inflector: Dry::Inflector.new,
65
- naming: Naming.new(inflector: inflector),
66
- generator: Generators::App::Action.new(fs: fs, inflector: inflector), **)
64
+ def initialize(
65
+ fs:, inflector:,
66
+ naming: Naming.new(inflector: inflector),
67
+ generator: Generators::App::Action.new(fs: fs, inflector: inflector),
68
+ **opts
69
+ )
70
+ super(fs: fs, inflector: inflector, **opts)
71
+
67
72
  @naming = naming
68
73
  @generator = generator
69
- super(fs: fs)
70
74
  end
71
75
 
72
76
  # rubocop:disable Metrics/ParameterLists
@@ -34,13 +34,12 @@ module Hanami
34
34
  # @since 2.0.0
35
35
  # @api private
36
36
  def initialize(
37
- fs: Hanami::CLI::Files.new,
38
- inflector: Dry::Inflector.new,
37
+ fs:, inflector:,
39
38
  generator: Generators::App::Part.new(fs: fs, inflector: inflector),
40
- **
39
+ **opts
41
40
  )
41
+ super(fs: fs, inflector: inflector, **opts)
42
42
  @generator = generator
43
- super(fs: fs)
44
43
  end
45
44
 
46
45
  # @since 2.0.0
@@ -23,10 +23,13 @@ module Hanami
23
23
 
24
24
  # @since 2.0.0
25
25
  # @api private
26
- def initialize(fs: Hanami::CLI::Files.new, inflector: Dry::Inflector.new,
27
- generator: Generators::App::Slice.new(fs: fs, inflector: inflector), **)
26
+ def initialize(
27
+ fs:, inflector:,
28
+ generator: Generators::App::Slice.new(fs: fs, inflector: inflector),
29
+ **opts
30
+ )
31
+ super(fs: fs, inflector: inflector, **opts)
28
32
  @generator = generator
29
- super(fs: fs)
30
33
  end
31
34
 
32
35
  # @since 2.0.0
@@ -33,13 +33,12 @@ module Hanami
33
33
  # @since 2.0.0
34
34
  # @api private
35
35
  def initialize(
36
- fs: Hanami::CLI::Files.new,
37
- inflector: Dry::Inflector.new,
36
+ fs:, inflector:,
38
37
  generator: Generators::App::View.new(fs: fs, inflector: inflector),
39
- **
38
+ **opts
40
39
  )
40
+ super(fs: fs, inflector: inflector, **opts)
41
41
  @generator = generator
42
- super(fs: fs)
43
42
  end
44
43
 
45
44
  # @since 2.0.0
@@ -46,8 +46,8 @@ module Hanami
46
46
 
47
47
  # @since 2.0.0
48
48
  # @api private
49
- def initialize(server: Hanami::CLI::Server.new)
50
- super()
49
+ def initialize(server: Hanami::CLI::Server.new, **opts)
50
+ super(**opts)
51
51
  @server = server
52
52
  end
53
53
 
@@ -63,17 +63,16 @@ module Hanami
63
63
  # @since 2.0.0
64
64
  # @api private
65
65
  def initialize(
66
- fs: Hanami::CLI::Files.new,
67
- inflector: Dry::Inflector.new,
66
+ fs:, inflector:,
68
67
  bundler: CLI::Bundler.new(fs: fs),
69
68
  generator: Generators::Gem::App.new(fs: fs, inflector: inflector),
70
69
  system_call: SystemCall.new,
71
- **other
70
+ **opts
72
71
  )
72
+ super(fs: fs, inflector: inflector, **opts)
73
73
  @bundler = bundler
74
74
  @generator = generator
75
75
  @system_call = system_call
76
- super(fs: fs, inflector: inflector, **other)
77
76
  end
78
77
 
79
78
  # rubocop:enable Metrics/ParameterLists
@@ -98,7 +97,7 @@ module Hanami
98
97
  bundler.install!
99
98
 
100
99
  unless skip_assets
101
- out.puts "Running npm install..."
100
+ out.puts "Running NPM install..."
102
101
  system_call.call("npm", ["install"]).tap do |result|
103
102
  unless result.successful?
104
103
  puts "NPM ERROR:"
@@ -35,6 +35,7 @@ module Hanami
35
35
  if context.bundled_assets?
36
36
  fs.write(fs.join(directory, "assets", "js", "app.js"), t("app_js.erb", context))
37
37
  fs.write(fs.join(directory, "assets", "css", "app.css"), t("app_css.erb", context))
38
+ fs.write(fs.join(directory, "assets", "images", "favicon.ico"), file("favicon.ico"))
38
39
  end
39
40
 
40
41
  # fs.write(fs.join(directory, "/entities.rb"), t("entities.erb", context))
@@ -64,6 +65,10 @@ module Hanami
64
65
  end
65
66
 
66
67
  alias_method :t, :template
68
+
69
+ def file(path)
70
+ File.read(File.join(__dir__, "slice", path))
71
+ end
67
72
  end
68
73
  end
69
74
  end
@@ -38,13 +38,13 @@ module Hanami
38
38
  # @since 2.1.0
39
39
  # @api private
40
40
  def stylesheet_erb_tag
41
- %(<%= stylesheet_tag "#{slice}/app" %>)
41
+ %(<%= stylesheet_tag "app" %>)
42
42
  end
43
43
 
44
44
  # @since 2.1.0
45
45
  # @api private
46
46
  def javascript_erb_tag
47
- %(<%= javascript_tag "#{slice}/app" %>)
47
+ %(<%= javascript_tag "app" %>)
48
48
  end
49
49
 
50
50
  private
@@ -53,7 +53,7 @@ module Hanami
53
53
  # @since 2.0.0
54
54
  # @api private
55
55
  def camelized_app_name
56
- inflector.camelize(app)
56
+ inflector.camelize(app).gsub(/[^\p{Alnum}]/, "")
57
57
  end
58
58
 
59
59
  # @since 2.0.0
@@ -4,6 +4,8 @@ await assets.run();
4
4
 
5
5
  // To provide additional esbuild (https://esbuild.github.io) options, use the following:
6
6
  //
7
+ // Read more at: https://guides.hanamirb.org/assets/customization/
8
+ //
7
9
  // await assets.run({
8
10
  // esbuildOptionsFn: (args, esbuildOptions) => {
9
11
  // // Add to esbuildOptions here. Use `args.watch` as a condition for different options for
@@ -3,13 +3,13 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  <%= hanami_gem("hanami") %>
6
- <%= hanami_gem("router") %>
7
- <%= hanami_gem("controller") %>
8
- <%= hanami_gem("validations") %>
9
- <%= hanami_gem("view") %>
10
6
  <%- if generate_assets? -%>
11
7
  <%= hanami_gem("assets") %>
12
8
  <%- end -%>
9
+ <%= hanami_gem("controller") %>
10
+ <%= hanami_gem("router") %>
11
+ <%= hanami_gem("validations") %>
12
+ <%= hanami_gem("view") %>
13
13
 
14
14
  gem "dry-types", "~> 1.0", ">= 1.6.1"
15
15
  gem "puma"
@@ -2,9 +2,6 @@
2
2
  "name": "<%= underscored_app_name %>",
3
3
  "private": true,
4
4
  "type": "module",
5
- "scripts": {
6
- "assets": "node config/assets.js"
7
- },
8
5
  "dependencies": {
9
6
  <%= hanami_assets_npm_package %>
10
7
  }
@@ -9,15 +9,16 @@ module Hanami
9
9
  class InteractiveSystemCall
10
10
  # @api private
11
11
  # @since 2.1.0
12
- def initialize(out: $stdout, err: $stderr)
12
+ def initialize(out: $stdout, err: $stderr, exit_after: true)
13
13
  @out = out
14
14
  @err = err
15
+ @exit_after = exit_after
15
16
  super()
16
17
  end
17
18
 
18
19
  # @api private
19
20
  # @since 2.1.0
20
- def call(cmd, *args, env: {})
21
+ def call(cmd, *args, env: {}, out_prefix: "")
21
22
  ::Bundler.with_unbundled_env do
22
23
  threads = []
23
24
  exit_status = 0
@@ -26,14 +27,14 @@ module Hanami
26
27
  Open3.popen3(env, command(cmd, *args)) do |_stdin, stdout, stderr, wait_thr|
27
28
  threads << Thread.new do
28
29
  stdout.each_line do |line|
29
- out.puts(line)
30
+ out.puts("#{out_prefix}#{line}")
30
31
  end
31
32
  rescue IOError # FIXME: Check if this is legit
32
33
  end
33
34
 
34
35
  threads << Thread.new do
35
36
  stderr.each_line do |line|
36
- err.puts(line)
37
+ err.puts("#{out_prefix}#{line}")
37
38
  end
38
39
  rescue IOError # FIXME: Check if this is legit
39
40
  end
@@ -44,7 +45,7 @@ module Hanami
44
45
  end
45
46
  # rubocop:enable Lint/SuppressedException
46
47
 
47
- exit(exit_status.exitstatus)
48
+ exit(exit_status.exitstatus) if @exit_after
48
49
  end
49
50
  end
50
51
 
@@ -6,6 +6,6 @@ module Hanami
6
6
  #
7
7
  # @api public
8
8
  # @since 2.0.0
9
- VERSION = "2.1.0.rc2"
9
+ VERSION = "2.1.0"
10
10
  end
11
11
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hanami-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0.rc2
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-11-08 00:00:00.000000000 Z
11
+ date: 2024-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -250,6 +250,7 @@ files:
250
250
  - lib/hanami/cli/generators/app/slice/app_js.erb
251
251
  - lib/hanami/cli/generators/app/slice/app_layout.erb
252
252
  - lib/hanami/cli/generators/app/slice/entities.erb
253
+ - lib/hanami/cli/generators/app/slice/favicon.ico
253
254
  - lib/hanami/cli/generators/app/slice/helpers.erb
254
255
  - lib/hanami/cli/generators/app/slice/keep.erb
255
256
  - lib/hanami/cli/generators/app/slice/repository.erb
@@ -326,11 +327,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
326
327
  version: '3.0'
327
328
  required_rubygems_version: !ruby/object:Gem::Requirement
328
329
  requirements:
329
- - - ">"
330
+ - - ">="
330
331
  - !ruby/object:Gem::Version
331
- version: 1.3.1
332
+ version: '0'
332
333
  requirements: []
333
- rubygems_version: 3.4.21
334
+ rubygems_version: 3.5.6
334
335
  signing_key:
335
336
  specification_version: 4
336
337
  summary: Hanami CLI