shopify-cli 2.12.0 → 2.15.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.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +5 -0
  3. data/.github/CONTRIBUTING.md +1 -1
  4. data/.github/PULL_REQUEST_TEMPLATE.md +1 -1
  5. data/.github/workflows/shopify.yml +2 -1
  6. data/.github/workflows/stale.yml +41 -0
  7. data/.rubocop.yml +1 -1
  8. data/.ruby-version +1 -1
  9. data/CHANGELOG.md +36 -0
  10. data/Gemfile.lock +18 -18
  11. data/Rakefile +16 -0
  12. data/bin/shopify +4 -4
  13. data/dev.yml +1 -1
  14. data/ext/javy/hashes/javy-arm-macos-v0.2.0.gz.sha256 +1 -0
  15. data/ext/javy/hashes/javy-arm-macos-v0.2.1.gz.sha256 +1 -0
  16. data/ext/javy/hashes/javy-x86_64-linux-v0.2.0.gz.sha256 +1 -0
  17. data/ext/javy/hashes/javy-x86_64-linux-v0.2.1.gz.sha256 +1 -0
  18. data/ext/javy/hashes/javy-x86_64-macos-v0.2.0.gz.sha256 +1 -0
  19. data/ext/javy/hashes/javy-x86_64-macos-v0.2.1.gz.sha256 +1 -0
  20. data/ext/javy/hashes/javy-x86_64-windows-v0.2.0.gz.sha256 +1 -0
  21. data/ext/javy/hashes/javy-x86_64-windows-v0.2.1.gz.sha256 +1 -0
  22. data/ext/javy/version +1 -1
  23. data/lib/project_types/extension/features/argo_setup_steps.rb +4 -6
  24. data/lib/project_types/extension/models/npm_package.rb +19 -1
  25. data/lib/project_types/extension/models/server_config/development_renderer.rb +4 -3
  26. data/lib/project_types/extension/models/specification_handlers/checkout_ui_extension.rb +13 -0
  27. data/lib/project_types/extension/tasks/configure_features.rb +15 -2
  28. data/lib/project_types/extension/tasks/convert_server_config.rb +2 -1
  29. data/lib/project_types/script/cli.rb +0 -4
  30. data/lib/project_types/script/commands/create.rb +4 -4
  31. data/lib/project_types/script/config/extension_points.yml +0 -6
  32. data/lib/project_types/script/errors.rb +1 -1
  33. data/lib/project_types/script/forms/create.rb +7 -7
  34. data/lib/project_types/script/layers/application/build_script.rb +9 -26
  35. data/lib/project_types/script/layers/application/create_script.rb +9 -10
  36. data/lib/project_types/script/layers/application/project_dependencies.rb +12 -14
  37. data/lib/project_types/script/layers/application/push_script.rb +14 -10
  38. data/lib/project_types/script/layers/domain/errors.rb +3 -3
  39. data/lib/project_types/script/layers/domain/push_package.rb +6 -0
  40. data/lib/project_types/script/layers/domain/script_config.rb +2 -4
  41. data/lib/project_types/script/layers/domain/script_project.rb +3 -2
  42. data/lib/project_types/script/layers/infrastructure/languages/project_creator.rb +0 -16
  43. data/lib/project_types/script/layers/infrastructure/languages/task_runner.rb +0 -1
  44. data/lib/project_types/script/layers/infrastructure/languages/typescript_project_creator.rb +19 -4
  45. data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +2 -10
  46. data/lib/project_types/script/layers/infrastructure/languages/wasm_project_creator.rb +0 -3
  47. data/lib/project_types/script/layers/infrastructure/languages/wasm_task_runner.rb +1 -1
  48. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +3 -21
  49. data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +14 -26
  50. data/lib/project_types/script/layers/infrastructure/script_service.rb +4 -2
  51. data/lib/project_types/script/messages/messages.rb +9 -9
  52. data/lib/project_types/script/ui/error_handler.rb +4 -4
  53. data/lib/project_types/script/ui/strict_spinner.rb +4 -6
  54. data/lib/project_types/theme/cli.rb +2 -0
  55. data/lib/project_types/theme/commands/common/root_helper.rb +11 -5
  56. data/lib/project_types/theme/commands/list.rb +34 -0
  57. data/lib/project_types/theme/commands/open.rb +65 -0
  58. data/lib/project_types/theme/commands/package.rb +1 -0
  59. data/lib/project_types/theme/commands/pull.rb +4 -4
  60. data/lib/project_types/theme/commands/push.rb +4 -4
  61. data/lib/project_types/theme/conversions/base_glob.rb +20 -5
  62. data/lib/project_types/theme/forms/select.rb +11 -39
  63. data/lib/project_types/theme/messages/messages.rb +33 -2
  64. data/lib/project_types/theme/presenters/theme_presenter.rb +48 -0
  65. data/lib/project_types/theme/presenters/themes_presenter.rb +32 -0
  66. data/lib/shopify_cli/api.rb +1 -1
  67. data/lib/shopify_cli/command.rb +1 -7
  68. data/lib/shopify_cli/commands/app/deploy.rb +0 -1
  69. data/lib/shopify_cli/constants.rb +2 -2
  70. data/lib/shopify_cli/context.rb +13 -15
  71. data/lib/shopify_cli/core/entry_point.rb +1 -1
  72. data/lib/shopify_cli/core/monorail.rb +14 -6
  73. data/lib/shopify_cli/environment.rb +19 -11
  74. data/lib/shopify_cli/exception_reporter.rb +2 -0
  75. data/lib/shopify_cli/messages/messages.rb +5 -5
  76. data/lib/shopify_cli/packager.rb +1 -1
  77. data/lib/shopify_cli/result.rb +14 -0
  78. data/lib/shopify_cli/services/app/create/node_service.rb +2 -14
  79. data/lib/shopify_cli/services/app/create/php_service.rb +1 -6
  80. data/lib/shopify_cli/services/app/create/rails_service.rb +5 -13
  81. data/lib/shopify_cli/theme/dev_server/hot_reload/remote_file_reloader.rb +5 -5
  82. data/lib/shopify_cli/theme/dev_server/watcher.rb +10 -2
  83. data/lib/shopify_cli/theme/development_theme.rb +2 -5
  84. data/lib/shopify_cli/theme/syncer.rb +27 -32
  85. data/lib/shopify_cli/theme/theme.rb +16 -27
  86. data/lib/shopify_cli/theme/theme_admin_api.rb +72 -0
  87. data/lib/shopify_cli/transform_data_structure.rb +3 -2
  88. data/lib/shopify_cli/version.rb +1 -1
  89. data/shipit.yml +3 -0
  90. data/shopify-cli.gemspec +9 -2
  91. data/shopify-dev +9 -11
  92. metadata +21 -9
  93. data/lib/project_types/rails/ruby.rb +0 -17
  94. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_project_creator.rb +0 -21
  95. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +0 -109
@@ -15,15 +15,16 @@ module Theme
15
15
 
16
16
  while next_index < argv.size
17
17
  element = argv[next_index]
18
- option = option_by_key[element]
18
+ key, value = key_value_tuple(element)
19
+ option = option_by_key[key]
19
20
 
20
21
  return element if option.nil?
21
22
 
22
23
  # Skip the option argument
23
- next_index += 1 unless option.arg.nil?
24
+ next_index += 1 if !option.arg.nil? && !value
24
25
 
25
26
  # PATTERN arguments take precedence over the `root`
26
- if option.arg =~ /PATTERN/
27
+ if option.arg =~ /PATTERN/ && !value
27
28
  next_index += 1 while option_argument?(argv, next_index, option_by_key)
28
29
  next
29
30
  end
@@ -37,7 +38,7 @@ module Theme
37
38
  private
38
39
 
39
40
  def default_argv(options)
40
- options.parser.default_argv
41
+ options.parser.default_argv.compact
41
42
  end
42
43
 
43
44
  def options_map(options)
@@ -57,7 +58,12 @@ module Theme
57
58
  return false unless next_index < argv.size
58
59
 
59
60
  element = argv[next_index]
60
- option_by_key[element].nil?
61
+ key, _value = key_value_tuple(element)
62
+ option_by_key[key].nil?
63
+ end
64
+
65
+ def key_value_tuple(element)
66
+ element.split("=")
61
67
  end
62
68
  end
63
69
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shopify_cli/theme/theme"
4
+ require "project_types/theme/presenters/themes_presenter"
5
+
6
+ module Theme
7
+ class Command
8
+ class List < ShopifyCLI::Command::SubCommand
9
+ recommend_default_ruby_range
10
+
11
+ def call(_args, _name)
12
+ @ctx.puts(@ctx.message("theme.list.title", shop))
13
+
14
+ themes_presenter.all.each do |theme|
15
+ @ctx.puts(" #{theme}")
16
+ end
17
+ end
18
+
19
+ def self.help
20
+ @ctx.message("theme.list.help", ShopifyCLI::TOOL_NAME, ShopifyCLI::TOOL_NAME)
21
+ end
22
+
23
+ private
24
+
25
+ def themes_presenter
26
+ Theme::Presenters::ThemesPresenter.new(@ctx, nil)
27
+ end
28
+
29
+ def shop
30
+ ShopifyCLI::AdminAPI.get_shop_or_abort(@ctx)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shopify_cli/theme/theme"
4
+ require "shopify_cli/theme/development_theme"
5
+
6
+ module Theme
7
+ class Command
8
+ class Open < ShopifyCLI::Command::SubCommand
9
+ recommend_default_ruby_range
10
+
11
+ options do |parser, flags|
12
+ parser.on("-t", "--theme=NAME_OR_ID") { |theme| flags[:theme] = theme }
13
+ parser.on("-l", "--live") { flags[:live] = true }
14
+ parser.on("-d", "--development") { flags[:development] = true }
15
+ end
16
+
17
+ def call(_args, _name)
18
+ theme = find_theme(**options.flags)
19
+
20
+ @ctx.puts(@ctx.message("theme.open.details", theme.name, theme.editor_url))
21
+ @ctx.open_url!(theme.preview_url)
22
+ end
23
+
24
+ def self.help
25
+ ShopifyCLI::Context.message("theme.open.help", ShopifyCLI::TOOL_NAME, ShopifyCLI::TOOL_NAME)
26
+ end
27
+
28
+ def find_theme(theme: nil, live: nil, development: nil, **_args)
29
+ return theme_by_identifier(theme) if theme
30
+ return live_theme if live
31
+ return development_theme if development
32
+
33
+ select_theme
34
+ end
35
+
36
+ def theme_by_identifier(identifier)
37
+ theme = ShopifyCLI::Theme::Theme.find_by_identifier(@ctx, identifier: identifier)
38
+ theme || not_found_error(identifier)
39
+ end
40
+
41
+ def development_theme
42
+ theme = ShopifyCLI::Theme::DevelopmentTheme.find(@ctx)
43
+ theme || not_found_error("development")
44
+ end
45
+
46
+ def live_theme
47
+ ShopifyCLI::Theme::Theme.live(@ctx)
48
+ end
49
+
50
+ def not_found_error(identifier)
51
+ @ctx.abort(@ctx.message("theme.open.theme_not_found", identifier))
52
+ end
53
+
54
+ def select_theme
55
+ form = Forms::Select.ask(
56
+ @ctx,
57
+ [],
58
+ title: @ctx.message("theme.open.select"),
59
+ root: nil
60
+ )
61
+ form&.theme
62
+ end
63
+ end
64
+ end
65
+ end
@@ -15,6 +15,7 @@ module Theme
15
15
  sections
16
16
  snippets
17
17
  templates
18
+ release-notes.md
18
19
  ]
19
20
 
20
21
  def call(args, _name)
@@ -26,11 +26,11 @@ module Theme
26
26
  parser.on("-d", "--development") { flags[:development] = true }
27
27
  parser.on("-o", "--only=PATTERN", Conversions::IncludeGlob) do |pattern|
28
28
  flags[:includes] ||= []
29
- flags[:includes] += pattern
29
+ flags[:includes] |= pattern
30
30
  end
31
31
  parser.on("-x", "--ignore=PATTERN", Conversions::IgnoreGlob) do |pattern|
32
32
  flags[:ignores] ||= []
33
- flags[:ignores] += pattern
33
+ flags[:ignores] |= pattern
34
34
  end
35
35
  end
36
36
 
@@ -45,8 +45,8 @@ module Theme
45
45
  ignore_filter.add_patterns(options.flags[:ignores]) if options.flags[:ignores]
46
46
 
47
47
  syncer = ShopifyCLI::Theme::Syncer.new(@ctx, theme: theme,
48
- include_filter: include_filter,
49
- ignore_filter: ignore_filter)
48
+ include_filter: include_filter,
49
+ ignore_filter: ignore_filter)
50
50
  begin
51
51
  syncer.start_threads
52
52
  CLI::UI::Frame.open(@ctx.message("theme.pull.pulling", theme.name, theme.id, theme.shop)) do
@@ -30,11 +30,11 @@ module Theme
30
30
  parser.on("-p", "--publish") { flags[:publish] = true }
31
31
  parser.on("-o", "--only=PATTERN", Conversions::IncludeGlob) do |pattern|
32
32
  flags[:includes] ||= []
33
- flags[:includes] += pattern
33
+ flags[:includes] |= pattern
34
34
  end
35
35
  parser.on("-x", "--ignore=PATTERN", Conversions::IgnoreGlob) do |pattern|
36
36
  flags[:ignores] ||= []
37
- flags[:ignores] += pattern
37
+ flags[:ignores] |= pattern
38
38
  end
39
39
  end
40
40
 
@@ -55,8 +55,8 @@ module Theme
55
55
  ignore_filter.add_patterns(options.flags[:ignores]) if options.flags[:ignores]
56
56
 
57
57
  syncer = ShopifyCLI::Theme::Syncer.new(@ctx, theme: theme,
58
- include_filter: include_filter,
59
- ignore_filter: ignore_filter)
58
+ include_filter: include_filter,
59
+ ignore_filter: ignore_filter)
60
60
  begin
61
61
  syncer.start_threads
62
62
  if options.flags[:json]
@@ -10,8 +10,22 @@ module Theme
10
10
 
11
11
  def convert(parser)
12
12
  argv = parser.default_argv
13
- option_index = argv.index { |v| options.include?(v) }
13
+ values = []
14
+
15
+ option_indexes(argv).each do |option_index|
16
+ values += option_values(argv, parser, option_index)
17
+ end
18
+
19
+ values
20
+ end
21
+
22
+ def options
23
+ raise "`#{self.class.name}#options` must be defined"
24
+ end
25
+
26
+ private
14
27
 
28
+ def option_values(argv, parser, option_index)
15
29
  return [] if option_index.nil?
16
30
 
17
31
  start_index = option_index + 1
@@ -26,12 +40,13 @@ module Theme
26
40
  values
27
41
  end
28
42
 
29
- def options
30
- raise "`#{self.class.name}#options` must be defined"
43
+ def option_indexes(argv)
44
+ argv
45
+ .each_with_index
46
+ .select { |item, _index| options.include?(item) }
47
+ .map(&:last)
31
48
  end
32
49
 
33
- private
34
-
35
50
  def options_map(parser)
36
51
  map = {}
37
52
  parser.top.list.each do |option|
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "project_types/theme/presenters/themes_presenter"
4
+
1
5
  module Theme
2
6
  module Forms
3
7
  class Select < ShopifyCLI::Form
@@ -6,53 +10,21 @@ module Theme
6
10
 
7
11
  def ask
8
12
  self.theme = CLI::UI::Prompt.ask(title, allow_empty: false) do |handler|
9
- themes.each do |theme|
13
+ theme_presenters.each do |presenter|
14
+ theme = presenter.theme
15
+
10
16
  next if exclude_roles&.include?(theme.role)
11
17
  next if !include_foreign_developments && theme.foreign_development?
12
- handler.option("#{theme.name} #{theme_tags(theme)}") { theme }
18
+
19
+ handler.option(presenter.to_s(:short)) { theme }
13
20
  end
14
21
  end
15
22
  end
16
23
 
17
24
  private
18
25
 
19
- def themes
20
- @themes ||= ShopifyCLI::Theme::Theme.all(@ctx, root: root)
21
- .sort_by { |theme| theme_sort_order(theme) }
22
- end
23
-
24
- def theme_sort_order(theme)
25
- case theme.role
26
- when "live"
27
- 0
28
- when "unpublished"
29
- 1
30
- when "development"
31
- 2
32
- else
33
- 3
34
- end
35
- end
36
-
37
- def theme_tags(theme)
38
- color = case theme.role
39
- when "live"
40
- "green"
41
- when "unpublished"
42
- "yellow"
43
- when "development"
44
- "blue"
45
- else
46
- "italic"
47
- end
48
-
49
- tags = ["{{#{color}:[#{theme.role}]}}"]
50
-
51
- if theme.current_development?
52
- tags << "{{cyan:[yours]}}}}"
53
- end
54
-
55
- tags.join(" ")
26
+ def theme_presenters
27
+ Theme::Presenters::ThemesPresenter.new(ctx, root).all
56
28
  end
57
29
  end
58
30
  end
@@ -7,6 +7,8 @@ module Theme
7
7
  Suite of commands for developing Shopify themes. See {{command:%1$s theme <command> --help}} for usage of each command.
8
8
  Usage: {{command:%1$s theme [ %2$s ]}}
9
9
  HELP
10
+ ensure_user_error: "You are not authorized to edit themes on %s.",
11
+ ensure_user_try_this: "Make sure you are a user of that store, and allowed to edit themes.",
10
12
 
11
13
  init: {
12
14
  help: <<~HELP,
@@ -87,7 +89,7 @@ module Theme
87
89
  {{info:View your theme:}}
88
90
  {{underline:%s}}
89
91
 
90
- {{info:Customize this theme in the Online Store Editor:}}
92
+ {{info:Customize this theme in the Theme Editor:}}
91
93
  {{underline:%s}}
92
94
  DONE
93
95
  name: "Theme name",
@@ -130,7 +132,7 @@ module Theme
130
132
  SERVING
131
133
  customize_or_preview: <<~CUSTOMIZE_OR_PREVIEW,
132
134
 
133
- Customize this theme in the Online Store Editor:
135
+ Customize this theme in the Theme Editor:
134
136
  {{green:%s}}
135
137
 
136
138
  Share this theme preview:
@@ -215,6 +217,35 @@ module Theme
215
217
  WARN
216
218
  theme_not_found: "Theme \"%s\" doesn't exist",
217
219
  },
220
+ open: {
221
+ select: "Select a theme to open",
222
+ theme_not_found: "Theme \"%s\" doesn't exist",
223
+ details: <<~DETAILS,
224
+ {{*}} {{bold:%s}}
225
+
226
+ Customize your theme in the Theme Editor:
227
+ {{green:%s}}
228
+
229
+ DETAILS
230
+ help: <<~HELP,
231
+ {{command:%s theme open}}: Opens the preview of your remote theme.
232
+
233
+ Usage: {{command:%s theme open}}
234
+
235
+ Options:
236
+ {{command:-t, --theme=NAME_OR_ID}} Theme ID or name of your theme.
237
+ {{command:-l, --live}} Open your live theme.
238
+ {{command:-d, --development}} Open your development theme.
239
+ HELP
240
+ },
241
+ list: {
242
+ title: "{{*}} List of {{bold:%s}} themes:",
243
+ help: <<~HELP,
244
+ {{command:%s theme list}}: Lists your remote themes.
245
+
246
+ Usage: {{command:%s theme list}}
247
+ HELP
248
+ },
218
249
  },
219
250
  }.freeze
220
251
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module Theme
6
+ module Presenters
7
+ class ThemePresenter
8
+ extend Forwardable
9
+
10
+ COLOR_BY_ROLE = {
11
+ "live" => "green",
12
+ "unpublished" => "yellow",
13
+ "development" => "blue",
14
+ }
15
+
16
+ attr_reader :theme
17
+
18
+ def_delegators :theme, :id, :name, :role
19
+
20
+ def initialize(theme)
21
+ @theme = theme
22
+ end
23
+
24
+ def to_s(mode = :long)
25
+ case mode
26
+ when :short
27
+ "{{bold:#{name} #{theme_tags}}}"
28
+ when :long
29
+ "{{green:##{id}}} {{bold:#{name} #{theme_tags}}}"
30
+ else
31
+ inspect
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def theme_tags
38
+ tags = ["{{#{tag_color}:[#{role}]}}"]
39
+ tags << "{{cyan:[yours]}}}}" if theme.current_development?
40
+ tags.join(" ")
41
+ end
42
+
43
+ def tag_color
44
+ COLOR_BY_ROLE[role] || "italic"
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "theme_presenter"
4
+
5
+ module Theme
6
+ module Presenters
7
+ class ThemesPresenter
8
+ ORDER_BY_ROLE = %w(live unpublished development)
9
+
10
+ def initialize(ctx, root)
11
+ @ctx = ctx
12
+ @root = root
13
+ end
14
+
15
+ def all
16
+ all_themes
17
+ .sort_by { |theme| order_by_role(theme) }
18
+ .map { |theme| ThemePresenter.new(theme) }
19
+ end
20
+
21
+ private
22
+
23
+ def order_by_role(theme)
24
+ ORDER_BY_ROLE.index(theme.role) || ORDER_BY_ROLE.size
25
+ end
26
+
27
+ def all_themes
28
+ ShopifyCLI::Theme::Theme.all(@ctx, root: @root)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -85,7 +85,7 @@ module ShopifyCLI
85
85
  end
86
86
  rescue Errno::ETIMEDOUT, Timeout::Error
87
87
  ctx.debug("timeout in #{method} #{uri} with X-Request-Id: #{headers["X-Request-Id"]}")
88
- raise APIRequestTimeoutError.new("Timeout")
88
+ raise APIRequestTimeoutError, "Timeout"
89
89
  end.retry_after(APIRequestRetriableError, retries: 3) do |e|
90
90
  sleep(1) if e.is_a?(APIRequestThrottledError)
91
91
  end
@@ -102,16 +102,10 @@ module ShopifyCLI
102
102
  def check_node_version
103
103
  return unless @compatible_node_range
104
104
 
105
- context = Context.new
106
- if context.which("node").nil?
107
- raise ShopifyCLI::Abort, context.message("core.errors.missing_node")
108
- end
109
-
110
105
  check_version(
111
106
  Environment.node_version,
112
107
  range: @compatible_node_range,
113
- runtime: "Node",
114
- context: context
108
+ runtime: "Node"
115
109
  )
116
110
  end
117
111
 
@@ -2,7 +2,6 @@ module ShopifyCLI
2
2
  module Commands
3
3
  class App
4
4
  class Deploy < ShopifyCLI::Command::AppSubCommand
5
- subcommand :Heroku, "heroku", "shopify_cli/commands/app/deploy/heroku"
6
5
  prerequisite_task :ensure_git_dependency
7
6
 
8
7
  recommend_default_ruby_range
@@ -61,12 +61,12 @@ module ShopifyCLI
61
61
  module SupportedVersions
62
62
  module Ruby
63
63
  FROM = "2.6.6"
64
- TO = "3.1.0"
64
+ TO = "3.2.0"
65
65
  end
66
66
 
67
67
  module Node
68
68
  FROM = "14.5.0"
69
- TO = "17.0.0"
69
+ TO = "18.0.0"
70
70
  end
71
71
  end
72
72
 
@@ -103,7 +103,7 @@ module ShopifyCLI
103
103
  # any command run by the context.
104
104
  attr_accessor :env
105
105
 
106
- def initialize(root: Dir.pwd, env: ($original_env || ENV).clone) # :nodoc:
106
+ def initialize(root: Dir.pwd, env: ($original_env || ENV).to_h) # :nodoc:
107
107
  self.root = root
108
108
  self.env = env
109
109
  end
@@ -164,7 +164,7 @@ module ShopifyCLI
164
164
 
165
165
  # will return true while tests are running, either locally or on CI
166
166
  def testing?
167
- ci? || ENV["TEST"]
167
+ ci? || ENV["SHOPIFY_CLI_TEST"]
168
168
  end
169
169
 
170
170
  ##
@@ -570,21 +570,19 @@ module ShopifyCLI
570
570
  trap("INFO", "DEFAULT")
571
571
 
572
572
  fork do
573
- begin
574
- r, w = IO.pipe
573
+ r, w = IO.pipe
574
+ @signal = false
575
+ trap("SIGINFO") do
576
+ @signal = true
577
+ w.write(0)
578
+ end
579
+ while r.read(1)
580
+ next unless @signal
575
581
  @signal = false
576
- trap("SIGINFO") do
577
- @signal = true
578
- w.write(0)
579
- end
580
- while r.read(1)
581
- next unless @signal
582
- @signal = false
583
- yield
584
- end
585
- rescue Interrupt
586
- exit(0)
582
+ yield
587
583
  end
584
+ rescue Interrupt
585
+ exit(0)
588
586
  end
589
587
  end
590
588
 
@@ -5,7 +5,7 @@ module ShopifyCLI
5
5
  module EntryPoint
6
6
  class << self
7
7
  def call(args, ctx = Context.new)
8
- if ctx.development?
8
+ if ctx.development? && !ctx.testing?
9
9
  ctx.warn(
10
10
  ctx.message("core.warning.development_version", File.join(ShopifyCLI::ROOT, "bin", ShopifyCLI::TOOL_NAME))
11
11
  )
@@ -17,11 +17,7 @@ module ShopifyCLI
17
17
 
18
18
  def log(name, args, &block) # rubocop:disable Lint/UnusedMethodArgument
19
19
  command, command_name = Commands::Registry.lookup_command(name)
20
- final_command = [command_name]
21
- if command
22
- subcommand, subcommand_name = command.subcommand_registry.lookup_command(args.first)
23
- final_command << subcommand_name if subcommand
24
- end
20
+ full_command = self.full_command(command, args, resolved_command: [command_name])
25
21
 
26
22
  start_time = now_in_milliseconds
27
23
  err = nil
@@ -35,9 +31,21 @@ module ShopifyCLI
35
31
  # If there's an error, we don't prompt from here and we let the exception
36
32
  # reporter do that.
37
33
  if report?(prompt: err.nil?)
38
- send_event(start_time, final_command, args - final_command, err&.message)
34
+ send_event(start_time, full_command, args - full_command, err&.message)
35
+ end
36
+ end
37
+ end
38
+
39
+ def full_command(command, args, resolved_command:)
40
+ resolved_command = resolved_command.dup
41
+ if command
42
+ subcommand, subcommand_name = command.subcommand_registry.lookup_command(args.first)
43
+ resolved_command << subcommand_name if subcommand
44
+ if subcommand&.subcommand_registry
45
+ resolved_command = full_command(subcommand, args.drop(1), resolved_command: resolved_command)
39
46
  end
40
47
  end
48
+ resolved_command
41
49
  end
42
50
 
43
51
  private
@@ -12,23 +12,31 @@ module ShopifyCLI
12
12
  ]
13
13
 
14
14
  def self.ruby_version(context: Context.new)
15
- out, err, stat = context.capture3('ruby -e "puts RUBY_VERSION"')
16
- raise ShopifyCLI::Abort, err unless stat.success?
17
- out = out.gsub('"', "")
18
- ::Semantic::Version.new(out.chomp)
15
+ output, status = context.capture2e("ruby", "--version")
16
+ raise ShopifyCLI::Abort, context.message("core.errors.missing_ruby") unless status.success?
17
+ version = output.match(/ruby (\d+\.\d+\.\d+)/)[1]
18
+ ::Semantic::Version.new(version)
19
19
  end
20
20
 
21
21
  def self.node_version(context: Context.new)
22
- out, err, stat = context.capture3("node", "--version")
23
- raise ShopifyCLI::Abort, err unless stat.success?
24
- out = out.gsub("v", "")
25
- ::Semantic::Version.new(out.chomp)
22
+ output, status = context.capture2e("node", "--version")
23
+ raise ShopifyCLI::Abort, context.message("core.errors.missing_node") unless status.success?
24
+ version = output.match(/v(\d+\.\d+\.\d+)/)[1]
25
+ ::Semantic::Version.new(version)
26
26
  end
27
27
 
28
28
  def self.npm_version(context: Context.new)
29
- out, err, stat = context.capture3("npm", "--version")
30
- raise ShopifyCLI::Abort, err unless stat.success?
31
- ::Semantic::Version.new(out.chomp)
29
+ output, status = context.capture2e("npm", "--version")
30
+ raise ShopifyCLI::Abort, context.message("core.errors.missing_npm") unless status.success?
31
+ version = output.match(/(\d+\.\d+\.\d+)/)[1]
32
+ ::Semantic::Version.new(version)
33
+ end
34
+
35
+ def self.rails_version(context: Context.new)
36
+ output, status = context.capture2e("rails", "--version")
37
+ context.abort(context.message("core.app.create.rails.error.install_failure", "rails")) unless status.success?
38
+ version = output.match(/Rails (\d+\.\d+\.\d+)/)[1]
39
+ ::Semantic::Version.new(version)
32
40
  end
33
41
 
34
42
  def self.interactive=(interactive)
@@ -46,6 +46,8 @@ module ShopifyCLI
46
46
  Bugsnag.notify(error) do |event|
47
47
  event.add_metadata(:device, metadata)
48
48
  end
49
+ rescue
50
+ nil
49
51
  end
50
52
 
51
53
  def self.report?(context:)