shopify-cli 1.10.0 → 1.11.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.github/PULL_REQUEST_TEMPLATE.md +1 -0
  3. data/CHANGELOG.md +9 -1
  4. data/Gemfile.lock +1 -1
  5. data/lib/project_types/extension/cli.rb +6 -2
  6. data/lib/project_types/extension/commands/serve.rb +69 -1
  7. data/lib/project_types/extension/commands/tunnel.rb +3 -1
  8. data/lib/project_types/extension/extension_project.rb +1 -0
  9. data/lib/project_types/extension/features/argo.rb +15 -24
  10. data/lib/project_types/extension/features/argo_runtime.rb +63 -0
  11. data/lib/project_types/extension/features/argo_serve.rb +35 -25
  12. data/lib/project_types/extension/features/argo_serve_options.rb +40 -0
  13. data/lib/project_types/extension/messages/messages.rb +3 -0
  14. data/lib/project_types/extension/models/npm_package.rb +14 -0
  15. data/lib/project_types/extension/models/specification.rb +1 -0
  16. data/lib/project_types/extension/models/specification_handlers/checkout_argo_extension.rb +18 -0
  17. data/lib/project_types/extension/models/specification_handlers/default.rb +28 -3
  18. data/lib/project_types/extension/tasks/choose_next_available_port.rb +36 -0
  19. data/lib/project_types/extension/tasks/configure_features.rb +2 -0
  20. data/lib/project_types/extension/tasks/find_npm_packages.rb +106 -0
  21. data/lib/project_types/script/cli.rb +1 -0
  22. data/lib/project_types/script/layers/domain/errors.rb +0 -2
  23. data/lib/project_types/script/layers/infrastructure/assemblyscript_project_creator.rb +12 -17
  24. data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +13 -7
  25. data/lib/project_types/script/layers/infrastructure/command_runner.rb +19 -0
  26. data/lib/project_types/script/layers/infrastructure/errors.rb +12 -3
  27. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +4 -4
  28. data/lib/project_types/script/layers/infrastructure/rust_project_creator.rb +9 -10
  29. data/lib/project_types/script/layers/infrastructure/rust_task_runner.rb +5 -6
  30. data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +2 -28
  31. data/lib/project_types/script/layers/infrastructure/script_service.rb +1 -1
  32. data/lib/project_types/script/messages/messages.rb +6 -4
  33. data/lib/project_types/script/tasks/ensure_env.rb +10 -2
  34. data/lib/project_types/script/ui/error_handler.rb +7 -6
  35. data/lib/shopify-cli/messages/messages.rb +47 -43
  36. data/lib/shopify-cli/method_object.rb +4 -4
  37. data/lib/shopify-cli/oauth.rb +7 -1
  38. data/lib/shopify-cli/partners_api/organizations.rb +3 -3
  39. data/lib/shopify-cli/tasks/select_org_and_shop.rb +6 -4
  40. data/lib/shopify-cli/tunnel.rb +22 -1
  41. data/lib/shopify-cli/version.rb +1 -1
  42. metadata +10 -4
  43. data/lib/project_types/extension/features/argo_renderer_package.rb +0 -47
@@ -27,28 +27,27 @@ module Script
27
27
 
28
28
  private
29
29
 
30
+ def command_runner
31
+ @command_runner ||= CommandRunner.new(ctx: ctx)
32
+ end
33
+
30
34
  def git_init
31
- out, status = ctx.capture2e("git init")
32
- raise Domain::Errors::ServiceFailureError, out unless status.success?
35
+ command_runner.call("git init")
33
36
  end
34
37
 
35
38
  def setup_remote
36
39
  repo = extension_point.sdks.rust.package
37
- out, status = ctx.capture2e("git remote add -f origin #{repo}")
38
- raise Domain::Errors::ServiceFailureError, out unless status.success?
40
+ command_runner.call("git remote add -f origin #{repo}")
39
41
  end
40
42
 
41
43
  def setup_sparse_checkout
42
44
  type = extension_point.type
43
- out, status = ctx.capture2e("git config core.sparsecheckout true")
44
- raise Domain::Errors::ServiceFailureError, out unless status.success?
45
- out, status = ctx.capture2e("echo #{type}/#{SAMPLE_PATH} >> .git/info/sparse-checkout")
46
- raise Domain::Errors::ServiceFailureError, out unless status.success?
45
+ command_runner.call("git config core.sparsecheckout true")
46
+ command_runner.call("echo #{type}/#{SAMPLE_PATH} >> .git/info/sparse-checkout")
47
47
  end
48
48
 
49
49
  def pull
50
- out, status = ctx.capture2e("git pull origin #{ORIGIN_BRANCH}")
51
- raise Domain::Errors::ServiceFailureError, out unless status.success?
50
+ command_runner.call("git pull origin #{ORIGIN_BRANCH}")
52
51
  end
53
52
 
54
53
  def clean
@@ -3,14 +3,14 @@ module Script
3
3
  module Layers
4
4
  module Infrastructure
5
5
  class RustTaskRunner
6
- attr_reader :ctx, :script_name
6
+ attr_reader :ctx
7
7
 
8
8
  BUILD_TARGET = "wasm32-unknown-unknown"
9
9
  METADATA_FILE = "build/metadata.json"
10
+ CARGO_BUILD_CMD = "cargo build --target=#{BUILD_TARGET} --release"
10
11
 
11
- def initialize(ctx, script_name)
12
+ def initialize(ctx, _)
12
13
  @ctx = ctx
13
- @script_name = script_name
14
14
  end
15
15
 
16
16
  def dependencies_installed?
@@ -42,12 +42,11 @@ module Script
42
42
  private
43
43
 
44
44
  def compile
45
- out, status = ctx.capture2e("cargo build --target=#{BUILD_TARGET} --release")
46
- raise Domain::Errors::ServiceFailureError, out unless status.success?
45
+ CommandRunner.new(ctx: ctx).call(CARGO_BUILD_CMD)
47
46
  end
48
47
 
49
48
  def bytecode
50
- binary_name = "#{script_name}.wasm"
49
+ binary_name = "script.wasm"
51
50
  binary_path = "target/#{BUILD_TARGET}/release/#{binary_name}"
52
51
  raise Errors::WebAssemblyBinaryNotFoundError unless ctx.file_exist?(binary_path)
53
52
 
@@ -13,15 +13,9 @@ module Script
13
13
  def create(script_name:, extension_point_type:, language:, no_config_ui:)
14
14
  validate_metadata!(extension_point_type, language)
15
15
 
16
- optional_identifiers = {}
17
16
  config_ui_file = nil
18
-
19
- unless no_config_ui
20
- optional_identifiers.merge!(config_ui_file: DEFAULT_CONFIG_UI_FILENAME)
21
- config_ui_file = ConfigUiRepository
22
- .new(ctx: ctx)
23
- .create(DEFAULT_CONFIG_UI_FILENAME, default_config_ui_content(script_name))
24
- end
17
+ optional_identifiers = {}
18
+ optional_identifiers.merge!(config_ui_file: DEFAULT_CONFIG_UI_FILENAME) unless no_config_ui
25
19
 
26
20
  ShopifyCli::Project.write(
27
21
  ctx,
@@ -131,17 +125,6 @@ module Script
131
125
  @project ||= ShopifyCli::Project.current(force_reload: true)
132
126
  end
133
127
 
134
- def default_config_ui_content(title)
135
- require "yaml" # takes 20ms, so deferred as late as possible.
136
- YAML.dump({
137
- "version" => 1,
138
- "inputMode" => "single",
139
- "title" => title,
140
- "description" => "",
141
- "fields" => [],
142
- })
143
- end
144
-
145
128
  def default_language
146
129
  Domain::ExtensionPoint::ExtensionPointAssemblyScriptSDK.language
147
130
  end
@@ -158,15 +141,6 @@ module Script
158
141
  include SmartProperties
159
142
  property! :ctx, accepts: ShopifyCli::Context
160
143
 
161
- def create(filename, content)
162
- File.write(filename, content)
163
-
164
- Domain::ConfigUi.new(
165
- filename: filename,
166
- content: content,
167
- )
168
- end
169
-
170
144
  def get(filename)
171
145
  return nil unless filename
172
146
 
@@ -40,7 +40,7 @@ module Script
40
40
  return resp_hash["data"]["appScriptUpdateOrCreate"]["appScript"]["uuid"] if user_errors.empty?
41
41
 
42
42
  if user_errors.any? { |e| e["tag"] == "already_exists_error" }
43
- raise Errors::ScriptRepushError, api_key
43
+ raise Errors::ScriptRepushError, uuid
44
44
  elsif (e = user_errors.any? { |err| err["tag"] == "config_ui_syntax_error" })
45
45
  raise Errors::ConfigUiSyntaxError, config_ui&.filename
46
46
  elsif (e = user_errors.find { |err| err["tag"] == "config_ui_missing_keys_error" })
@@ -78,8 +78,8 @@ module Script
78
78
 
79
79
  script_not_found_cause: "Couldn't find script %s for extension point %s",
80
80
 
81
- service_failure_cause: "Internal service error.",
82
- service_failure_help: "Ensure the 'shopify/scripts-toolchain-as' package is up to date.",
81
+ system_call_failure_cause: "An error was returned while running {{command:%{cmd}}}.",
82
+ system_call_failure_help: "Review the following error and try again.\n{{red:%{out}}}",
83
83
 
84
84
  metadata_validation_cause: "Invalid script extension metadata.",
85
85
  metadata_validation_help: "Ensure the 'shopify/scripts-toolchain-as' package is up to date.",
@@ -113,7 +113,7 @@ module Script
113
113
  graphql_error_cause: "An error was returned: %s.",
114
114
  graphql_error_help: "\nReview the error and try again.",
115
115
 
116
- script_repush_cause: "A script with the same extension point already exists on app (API key: %s).",
116
+ script_repush_cause: "A script with this UUID already exists (UUID: %s).",
117
117
  script_repush_help: "Use {{cyan:--force}} to replace the existing script.",
118
118
 
119
119
  shop_auth_cause: "Unable to authenticate with the store.",
@@ -193,9 +193,11 @@ module Script
193
193
  enabling: "Enabling",
194
194
  enabled: "Enabled",
195
195
  ensure_env: {
196
+ organization: "Partner organization {{green:%s (%s)}}.",
196
197
  organization_select: "Which partner organization do you want to use?",
198
+ app: "Script will be pushed to app {{green:%s}}.",
197
199
  app_select: "Which app do you want to push this script to?",
198
- ask_connect_to_existing_script: "This app has some scripts. Do you want to replace any of the "\
200
+ ask_connect_to_existing_script: "The selected app has some scripts. Do you want to replace any of the "\
199
201
  "existing scripts with the current script?",
200
202
  ask_which_script_to_connect_to: "Which script do you want to replace?",
201
203
  },
@@ -33,7 +33,9 @@ module Script
33
33
 
34
34
  orgs = ShopifyCli::PartnersAPI::Organizations.fetch_with_app(ctx)
35
35
  if orgs.count == 1
36
- orgs.first
36
+ default = orgs.first
37
+ ctx.puts(ctx.message("script.application.ensure_env.organization", default["businessName"], default["id"]))
38
+ default
37
39
  elsif orgs.count > 0
38
40
  CLI::UI::Prompt.ask(ctx.message("script.application.ensure_env.organization_select")) do |handler|
39
41
  orgs.each do |org|
@@ -46,8 +48,14 @@ module Script
46
48
  end
47
49
 
48
50
  def ask_app(apps)
51
+ unless ShopifyCli::Shopifolk.acting_as_shopify_organization?
52
+ apps = apps.select { |app| app["appType"] == "custom" }
53
+ end
54
+
49
55
  if apps.count == 1
50
- apps.first
56
+ default = apps.first
57
+ ctx.puts(ctx.message("script.application.ensure_env.app", default["title"]))
58
+ default
51
59
  elsif apps.count > 0
52
60
  CLI::UI::Prompt.ask(ctx.message("script.application.ensure_env.app_select")) do |handler|
53
61
  apps.each do |app|
@@ -110,11 +110,6 @@ module Script
110
110
  e.extension_point_type
111
111
  ),
112
112
  }
113
- when Layers::Domain::Errors::ServiceFailureError
114
- {
115
- cause_of_error: ShopifyCli::Context.message("script.error.service_failure_cause"),
116
- help_suggestion: ShopifyCli::Context.message("script.error.service_failure_help"),
117
- }
118
113
  when Layers::Domain::Errors::MetadataValidationError
119
114
  {
120
115
  cause_of_error: ShopifyCli::Context.message("script.error.metadata_validation_cause"),
@@ -211,9 +206,15 @@ module Script
211
206
  ),
212
207
  help_suggestion: ShopifyCli::Context.message("script.error.graphql_error_help"),
213
208
  }
209
+ when Layers::Infrastructure::Errors::SystemCallFailureError
210
+ {
211
+ cause_of_error: ShopifyCli::Context
212
+ .message("script.error.system_call_failure_cause", cmd: e.cmd),
213
+ help_suggestion: ShopifyCli::Context.message("script.error.system_call_failure_help", out: e.out),
214
+ }
214
215
  when Layers::Infrastructure::Errors::ScriptRepushError
215
216
  {
216
- cause_of_error: ShopifyCli::Context.message("script.error.script_repush_cause", e.api_key),
217
+ cause_of_error: ShopifyCli::Context.message("script.error.script_repush_cause", e.uuid),
217
218
  help_suggestion: ShopifyCli::Context.message("script.error.script_repush_help"),
218
219
  }
219
220
  when Layers::Infrastructure::Errors::ShopAuthenticationError
@@ -16,8 +16,8 @@ module ShopifyCli
16
16
  core: {
17
17
  connect: {
18
18
  help: <<~HELP,
19
- Connect (or re-connect) an existing project to a Shopify partner organization and/or a store. Creates or updates the {{green:.env}} file, and creates the {{green:.shopify-cli.yml}} file.
20
- Usage: {{command:%s connect}}
19
+ Connect (or re-connect) an existing project to a Shopify partner organization and/or a store. Creates or updates the {{green:.env}} file, and creates the {{green:.shopify-cli.yml}} file.
20
+ Usage: {{command:%s connect}}
21
21
  HELP
22
22
 
23
23
  already_connected_warning: "{{yellow:! This app appears to be already connected}}",
@@ -27,15 +27,15 @@ module ShopifyCli
27
27
 
28
28
  context: {
29
29
  open_url: <<~OPEN,
30
- Please open this URL in your browser:
31
- {{green:%s}}
30
+ Please open this URL in your browser:
31
+ {{green:%s}}
32
32
  OPEN
33
33
  },
34
34
 
35
35
  create: {
36
36
  help: <<~HELP,
37
- Create a new project.
38
- Usage: {{command:%s create [ %s ]}}
37
+ Create a new project.
38
+ Usage: {{command:%s create [ %s ]}}
39
39
  HELP
40
40
 
41
41
  error: {
@@ -53,13 +53,13 @@ module ShopifyCli
53
53
 
54
54
  config: {
55
55
  help: <<~HELP,
56
- Change configuration of how the CLI operates
57
- Usage: {{command:%s config [ feature | analytics ] }}
56
+ Change configuration of how the CLI operates
57
+ Usage: {{command:%s config [ feature | analytics ] }}
58
58
  HELP
59
59
  feature: {
60
60
  help: <<~HELP,
61
- Change configuration of various features
62
- Usage: {{command:%s config [ feature ] [ feature_name ] }}
61
+ Change configuration of various features
62
+ Usage: {{command:%s config [ feature ] [ feature_name ] }}
63
63
  HELP
64
64
  enabled: "{{v}} feature {{green:%s}} has been enabled",
65
65
  disabled: "{{v}} feature {{green:%s}} has been disabled",
@@ -68,8 +68,8 @@ module ShopifyCli
68
68
  },
69
69
  analytics: {
70
70
  help: <<~HELP,
71
- Opt in/out of anonymous usage reporting
72
- Usage: {{command:%s config [ analytics ] }}
71
+ Opt in/out of anonymous usage reporting
72
+ Usage: {{command:%s config [ analytics ] }}
73
73
  HELP
74
74
  enabled: "{{v}} analytics have been enabled",
75
75
  disabled: "{{v}} analytics have been disabled",
@@ -97,9 +97,9 @@ module ShopifyCli
97
97
  },
98
98
 
99
99
  preamble: <<~MESSAGE,
100
- Use {{command:%s help <command>}} to display detailed information about a specific command.
100
+ Use {{command:%s help <command>}} to display detailed information about a specific command.
101
101
 
102
- {{bold:Available core commands:}}
102
+ {{bold:Available core commands:}}
103
103
 
104
104
  MESSAGE
105
105
  },
@@ -131,8 +131,8 @@ module ShopifyCli
131
131
 
132
132
  logout: {
133
133
  help: <<~HELP,
134
- Log out of a currently authenticated partner organization and store, or clear invalid credentials
135
- Usage: {{command:%s logout}}
134
+ Log out of a currently authenticated partner organization and store, or clear invalid credentials
135
+ Usage: {{command:%s logout}}
136
136
  HELP
137
137
 
138
138
  success: "Logged out of partner organization and store",
@@ -154,6 +154,7 @@ module ShopifyCli
154
154
  location: {
155
155
  admin: "development store",
156
156
  partner: "Shopify Partners account",
157
+ shopifolk: "{{green:Shopify Employee account}}",
157
158
  },
158
159
  authentication_required:
159
160
  "{{i}} Authentication required. Login to the URL below with your %s credentials to continue.",
@@ -175,8 +176,8 @@ module ShopifyCli
175
176
  org_name_and_id: "%s (%s)",
176
177
  error: {
177
178
  account_not_found: <<~MESSAGE,
178
- {{x}} error: Your account was not found. Please sign up at https://partners.shopify.com/signup
179
- For authentication issues, run {{command:%s logout}} to clear invalid credentials
179
+ {{x}} error: Your account was not found. Please sign up at https://partners.shopify.com/signup
180
+ For authentication issues, run {{command:%s logout}} to clear invalid credentials
180
181
  MESSAGE
181
182
  },
182
183
  },
@@ -196,16 +197,16 @@ module ShopifyCli
196
197
  },
197
198
  populating: "Populating %d %ss...",
198
199
  completion_message: <<~COMPLETION_MESSAGE,
199
- Successfully added %d %s to {{green:%s}}
200
- {{*}} View all %ss at {{underline:%s%ss}}
200
+ Successfully added %d %s to {{green:%s}}
201
+ {{*}} View all %ss at {{underline:%s%ss}}
201
202
  COMPLETION_MESSAGE
202
203
  },
203
204
 
204
205
  project: {
205
206
  error: {
206
207
  not_in_project: <<~MESSAGE,
207
- {{x}} You are not in a Shopify app project
208
- {{yellow:{{*}}}}{{reset: Run}}{{cyan: shopify create}}{{reset: to create your app}}
208
+ {{x}} You are not in a Shopify app project
209
+ {{yellow:{{*}}}}{{reset: Run}}{{cyan: shopify create}}{{reset: to create your app}}
209
210
  MESSAGE
210
211
  },
211
212
  },
@@ -226,10 +227,10 @@ module ShopifyCli
226
227
 
227
228
  system: {
228
229
  help: <<~HELP,
229
- Print details about the development system.
230
- Usage: {{command:%s system [all]}}
230
+ Print details about the development system.
231
+ Usage: {{command:%s system [all]}}
231
232
 
232
- {{cyan:all}}: displays more details about development system and environment
233
+ {{cyan:all}}: displays more details about development system and environment
233
234
 
234
235
  HELP
235
236
 
@@ -240,8 +241,8 @@ module ShopifyCli
240
241
  header: "{{bold:Shopify App CLI}}",
241
242
  const: "%17s = %s",
242
243
  ruby_header: <<~RUBY_MESSAGE,
243
- {{bold:Ruby (via RbConfig)}}
244
- %s
244
+ {{bold:Ruby (via RbConfig)}}
245
+ %s
245
246
  RUBY_MESSAGE
246
247
  rb_config: "%-25s - RbConfig[\"%s\"]",
247
248
  command_header: "{{bold:Commands}}",
@@ -268,8 +269,8 @@ module ShopifyCli
268
269
  ensure_env: {
269
270
  organization_select: "To which partner organization does this project belong?",
270
271
  no_development_stores: <<~MESSAGE,
271
- No development stores available.
272
- Visit {{underline:https://partners.shopify.com/%d/stores}} to create one
272
+ No development stores available.
273
+ Visit {{underline:https://partners.shopify.com/%d/stores}} to create one
273
274
  MESSAGE
274
275
  development_store_select: "Which development store would you like to use?",
275
276
  app_select: "To which app does this project belong?",
@@ -306,7 +307,10 @@ module ShopifyCli
306
307
  no_development_stores: "{{x}} No Development Stores available.",
307
308
  no_organizations: "No partner organizations available.",
308
309
  organization_not_found: "Cannot find a partner organization with that ID",
309
- partners_notice: "Please visit https://partners.shopify.com/ to create a partners account",
310
+ shopifolk_notice: <<~MESSAGE,
311
+ {{i}} As a {{green:Shopify}} employee, the authentication should take you to the Shopify Okta login,
312
+ NOT the Partner account login. Please run {{command:%s logout}} and try again.
313
+ MESSAGE
310
314
  },
311
315
  first_party: "Are you working on a 1P (1st Party) app?",
312
316
  identified_as_shopify: "We've identified you as a {{green:Shopify}} employee.",
@@ -327,9 +331,9 @@ module ShopifyCli
327
331
 
328
332
  not_running: "{{green:x}} ngrok tunnel not running",
329
333
  signup_suggestion: <<~MESSAGE,
330
- {{*}} To avoid tunnels that timeout, it is recommended to signup for a free ngrok
331
- account at {{underline:https://ngrok.com/signup}}. After you signup, install your
332
- personalized authorization token using {{command:%s tunnel auth <token>}}.
334
+ {{*}} To avoid tunnels that timeout, it is recommended to signup for a free ngrok
335
+ account at {{underline:https://ngrok.com/signup}}. After you signup, install your
336
+ personalized authorization token using {{command:%s tunnel auth <token>}}.
333
337
  MESSAGE
334
338
  start: "{{v}} ngrok tunnel running at {{underline:%s}}",
335
339
  start_with_account: "{{v}} ngrok tunnel running at {{underline:%s}}, with account %s",
@@ -341,31 +345,31 @@ module ShopifyCli
341
345
 
342
346
  version: {
343
347
  help: <<~HELP,
344
- Prints version number.
345
- Usage: {{command:%s version}}
348
+ Prints version number.
349
+ Usage: {{command:%s version}}
346
350
  HELP
347
351
  },
348
352
 
349
353
  warning: {
350
354
  development_version: <<~DEVELOPMENT,
351
- {{*}} {{yellow:You are running a development version of the CLI at:}}
352
- {{yellow:%s}}
355
+ {{*}} {{yellow:You are running a development version of the CLI at:}}
356
+ {{yellow:%s}}
353
357
 
354
358
  DEVELOPMENT
355
359
 
356
360
  shell_shim: <<~MESSAGE,
357
- {{x}} This version of Shopify App CLI is no longer supported. You’ll need to migrate to the new CLI version to continue.
361
+ {{x}} This version of Shopify App CLI is no longer supported. You’ll need to migrate to the new CLI version to continue.
358
362
 
359
- Please visit this page for complete instructions:
360
- {{underline:https://shopify.dev/tools/cli/troubleshooting#migrate-from-a-legacy-version}}
363
+ Please visit this page for complete instructions:
364
+ {{underline:https://shopify.dev/tools/cli/troubleshooting#migrate-from-a-legacy-version}}
361
365
 
362
366
  MESSAGE
363
367
 
364
368
  new_version: <<~MESSAGE,
365
- {{*}} {{yellow:A new version of Shopify App CLI is available! You have version %s and the latest version is %s.
369
+ {{*}} {{yellow:A new version of Shopify App CLI is available! You have version %s and the latest version is %s.
366
370
 
367
- To upgrade, follow the instructions for the package manager you’re using:
368
- {{underline:https://shopify.dev/tools/cli/troubleshooting#upgrade-shopify-app-cli}}}}
371
+ To upgrade, follow the instructions for the package manager you’re using:
372
+ {{underline:https://shopify.dev/tools/cli/troubleshooting#upgrade-shopify-app-cli}}}}
369
373
 
370
374
  MESSAGE
371
375
  },
@@ -52,8 +52,8 @@ module ShopifyCli
52
52
  # invokes the original `call` implementation and wraps its return value
53
53
  # into a result object.
54
54
  #
55
- def call(*args, **kwargs)
56
- Result.wrap { super(*args, **kwargs) }.call
55
+ def call(*args, **kwargs, &block)
56
+ Result.wrap { kwargs.any? ? super(*args, **kwargs, &block) : super(*args, &block) }.call
57
57
  end
58
58
  end
59
59
 
@@ -64,10 +64,10 @@ module ShopifyCli
64
64
  # initializer or to `call`. If the keyword argument matches the name of
65
65
  # property, it is forwarded to the initializer, otherwise to call.
66
66
  #
67
- def call(*args, **kwargs)
67
+ def call(*args, **kwargs, &block)
68
68
  properties.keys.yield_self do |properties|
69
69
  new(**kwargs.slice(*properties))
70
- .call(*args, **kwargs.slice(*(kwargs.keys - properties)))
70
+ .call(*args, **kwargs.slice(*(kwargs.keys - properties)), &block)
71
71
  end
72
72
  end
73
73