shopify-cli 1.2.0 → 1.5.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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +2 -2
  3. data/.github/CONTRIBUTING.md +9 -1
  4. data/.github/PULL_REQUEST_TEMPLATE.md +9 -1
  5. data/.github/workflows/release.yml +61 -0
  6. data/.github/workflows/triage.yml +22 -0
  7. data/.gitignore +0 -1
  8. data/.rubocop.yml +61 -8
  9. data/.rubocop_todo.yml +11 -0
  10. data/CHANGELOG.md +25 -0
  11. data/Gemfile +3 -2
  12. data/Gemfile.lock +39 -37
  13. data/RELEASING.md +19 -29
  14. data/docs/core/index.md +16 -0
  15. data/lib/project_types/extension/cli.rb +7 -2
  16. data/lib/project_types/extension/commands/register.rb +1 -1
  17. data/lib/project_types/extension/features/argo/admin.rb +20 -0
  18. data/lib/project_types/extension/features/argo/base.rb +129 -0
  19. data/lib/project_types/extension/features/argo/checkout.rb +20 -0
  20. data/lib/project_types/extension/messages/messages.rb +8 -2
  21. data/lib/project_types/extension/models/type.rb +4 -0
  22. data/lib/project_types/extension/models/types/checkout_post_purchase.rb +2 -2
  23. data/lib/project_types/extension/models/types/product_subscription.rb +24 -0
  24. data/lib/project_types/node/cli.rb +4 -1
  25. data/lib/project_types/node/commands/connect.rb +15 -0
  26. data/lib/project_types/node/commands/create.rb +8 -4
  27. data/lib/project_types/node/messages/messages.rb +7 -6
  28. data/lib/project_types/rails/cli.rb +4 -1
  29. data/lib/project_types/rails/commands/connect.rb +15 -0
  30. data/lib/project_types/rails/commands/create.rb +8 -4
  31. data/lib/project_types/rails/messages/messages.rb +7 -4
  32. data/lib/project_types/script/cli.rb +2 -1
  33. data/lib/project_types/script/commands/enable.rb +12 -4
  34. data/lib/project_types/script/config/extension_points.yml +13 -12
  35. data/lib/project_types/script/errors.rb +4 -0
  36. data/lib/project_types/script/layers/application/build_script.rb +12 -16
  37. data/lib/project_types/script/layers/domain/errors.rb +3 -0
  38. data/lib/project_types/script/layers/domain/extension_point.rb +0 -1
  39. data/lib/project_types/script/layers/infrastructure/assemblyscript_project_creator.rb +15 -50
  40. data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +28 -7
  41. data/lib/project_types/script/layers/infrastructure/errors.rb +16 -0
  42. data/lib/project_types/script/layers/infrastructure/script_repository.rb +0 -12
  43. data/lib/project_types/script/layers/infrastructure/script_service.rb +2 -0
  44. data/lib/project_types/script/messages/messages.rb +23 -5
  45. data/lib/project_types/script/ui/error_handler.rb +31 -6
  46. data/lib/shopify-cli/api.rb +8 -10
  47. data/lib/shopify-cli/commands/config.rb +57 -1
  48. data/lib/shopify-cli/commands/connect.rb +32 -15
  49. data/lib/shopify-cli/commands/system.rb +9 -0
  50. data/lib/shopify-cli/core/entry_point.rb +1 -1
  51. data/lib/shopify-cli/core/monorail.rb +6 -4
  52. data/lib/shopify-cli/http_request.rb +15 -0
  53. data/lib/shopify-cli/js_deps.rb +1 -1
  54. data/lib/shopify-cli/js_system.rb +22 -5
  55. data/lib/shopify-cli/messages/messages.rb +44 -9
  56. data/lib/shopify-cli/partners_api.rb +17 -1
  57. data/lib/shopify-cli/process_supervision.rb +1 -1
  58. data/lib/shopify-cli/project.rb +12 -8
  59. data/lib/shopify-cli/project_type.rb +17 -1
  60. data/lib/shopify-cli/shopifolk.rb +86 -0
  61. data/lib/shopify-cli/task.rb +8 -0
  62. data/lib/shopify-cli/tasks/create_api_client.rb +13 -2
  63. data/lib/shopify-cli/tasks/ensure_env.rb +3 -0
  64. data/lib/shopify-cli/tasks/select_org_and_shop.rb +4 -0
  65. data/lib/shopify-cli/tunnel.rb +1 -1
  66. data/lib/shopify-cli/version.rb +1 -1
  67. data/lib/shopify_cli.rb +1 -0
  68. metadata +13 -5
  69. data/lib/project_types/extension/features/argo.rb +0 -48
  70. data/lib/project_types/extension/models/types/subscription_management.rb +0 -20
  71. data/lib/project_types/script/layers/infrastructure/assemblyscript_tsconfig.rb +0 -38
@@ -18,6 +18,10 @@ module Script
18
18
  invalid_context_cause: "Your .shopify-cli.yml file is not correct.",
19
19
  invalid_context_help: "See https://help.shopify.com",
20
20
 
21
+ invalid_config_props_cause: "{{command:--config_props}} is formatted incorrectly.",
22
+ invalid_config_props_help: "Try again using this format: "\
23
+ "{{cyan:--config_props='name1:value1, name2:value2'}}",
24
+
21
25
  invalid_script_name_cause: "Invalid script name.",
22
26
  invalid_script_name_help: "Replace or remove unsupported characters. Valid characters "\
23
27
  "are numbers, letters, hyphens, or underscores.",
@@ -45,9 +49,7 @@ module Script
45
49
 
46
50
  app_not_installed_cause: "App not installed on store.",
47
51
 
48
- app_script_not_pushed_help: "Push the script and then try this command again.",
49
-
50
- app_script_undefined_help: "Push script to app.",
52
+ app_script_not_pushed_help: "Script isn't on the app. Run {{command:shopify push}}, and then try again.",
51
53
 
52
54
  build_error_cause: "Something went wrong while building the script.",
53
55
  build_error_help: "Correct the errors and try again.",
@@ -55,6 +57,9 @@ module Script
55
57
  dependency_install_cause: "Something went wrong while installing the dependencies that are needed.",
56
58
  dependency_install_help: "Correct the errors and try again.",
57
59
 
60
+ failed_api_request_cause: "Something went wrong while communicating with Shopify.",
61
+ failed_api_request_help: "Try again.",
62
+
58
63
  forbidden_error_cause: "You do not have permission to do this action.",
59
64
 
60
65
  graphql_error_cause: "An error was returned: %s.",
@@ -72,8 +77,21 @@ module Script
72
77
 
73
78
  shop_script_undefined_cause: "Script is already turned off in store.",
74
79
 
75
- packages_outdated_cause: "The following npm packages are out of date: %s.",
76
- packages_outdated_help: "Run `npm update` to update them.",
80
+ packages_outdated_cause: "These npm packages are out of date: %s.",
81
+ packages_outdated_help: "To update them, run {{cyan:npm install --save-dev %s}}.",
82
+
83
+ invalid_build_script: "Invalid build script.",
84
+ build_script_not_found: "Build script not found.",
85
+ # rubocop:disable Layout/LineLength
86
+ build_script_suggestion: "Root package.json needs a script named build, which" \
87
+ "uses @shopify/scripts-toolchain-as to compile to WebAssembly.\n" \
88
+ "Example:\n" \
89
+ "build: npx shopify-scripts-toolchain-as build --src src/script.ts --binary <script_name>.wasm -- --lib node_modules --optimize --use Date=",
90
+
91
+ web_assembly_binary_not_found: "WebAssembly binary not found.",
92
+ web_assembly_binary_not_found_suggestion: "No WebAssembly binary found." \
93
+ "Check that your build npm script outputs the generated binary to the root of the directory." \
94
+ "Generated binary should match the script name: <script_name>.wasm",
77
95
  },
78
96
 
79
97
  create: {
@@ -44,6 +44,11 @@ module Script
44
44
  cause_of_error: ShopifyCli::Context.message('script.error.invalid_context_cause'),
45
45
  help_suggestion: ShopifyCli::Context.message('script.error.invalid_context_help'),
46
46
  }
47
+ when Errors::InvalidConfigProps
48
+ {
49
+ cause_of_error: ShopifyCli::Context.message('script.error.invalid_config_props_cause'),
50
+ help_suggestion: ShopifyCli::Context.message('script.error.invalid_config_props_help'),
51
+ }
47
52
  when Errors::InvalidConfigYAMLError
48
53
  {
49
54
  cause_of_error: ShopifyCli::Context.message('script.error.invalid_config', e.config_file),
@@ -96,14 +101,11 @@ module Script
96
101
  {
97
102
  cause_of_error: ShopifyCli::Context.message('script.error.app_not_installed_cause'),
98
103
  }
99
- when Layers::Infrastructure::Errors::AppScriptNotPushedError
104
+ when Layers::Infrastructure::Errors::AppScriptNotPushedError,
105
+ Layers::Infrastructure::Errors::AppScriptUndefinedError
100
106
  {
101
107
  cause_of_error: ShopifyCli::Context.message('script.error.app_script_not_pushed_help'),
102
108
  }
103
- when Layers::Infrastructure::Errors::AppScriptUndefinedError
104
- {
105
- help_suggestion: ShopifyCli::Context.message('script.error.app_script_undefined_help'),
106
- }
107
109
  when Layers::Infrastructure::Errors::BuildError
108
110
  {
109
111
  cause_of_error: ShopifyCli::Context.message('script.error.build_error_cause'),
@@ -114,6 +116,11 @@ module Script
114
116
  cause_of_error: ShopifyCli::Context.message('script.error.dependency_install_cause'),
115
117
  help_suggestion: ShopifyCli::Context.message('script.error.dependency_install_help'),
116
118
  }
119
+ when Layers::Infrastructure::Errors::EmptyResponseError
120
+ {
121
+ cause_of_error: ShopifyCli::Context.message('script.error.failed_api_request_cause'),
122
+ help_suggestion: ShopifyCli::Context.message('script.error.failed_api_request_help'),
123
+ }
117
124
  when Layers::Infrastructure::Errors::ForbiddenError
118
125
  {
119
126
  cause_of_error: ShopifyCli::Context.message('script.error.forbidden_error_cause'),
@@ -148,7 +155,25 @@ module Script
148
155
  'script.error.packages_outdated_cause',
149
156
  e.outdated_packages.join(', ')
150
157
  ),
151
- help_suggestion: ShopifyCli::Context.message('script.error.packages_outdated_help'),
158
+ help_suggestion: ShopifyCli::Context.message(
159
+ 'script.error.packages_outdated_help',
160
+ e.outdated_packages.collect { |package| "#{package}@latest" }.join(' ')
161
+ ),
162
+ }
163
+ when Layers::Infrastructure::Errors::BuildScriptNotFoundError
164
+ {
165
+ cause_of_error: ShopifyCli::Context.message('script.error.build_script_not_found'),
166
+ help_suggestion: ShopifyCli::Context.message('script.error.build_script_suggestion'),
167
+ }
168
+ when Layers::Infrastructure::Errors::InvalidBuildScriptError
169
+ {
170
+ cause_of_error: ShopifyCli::Context.message('script.error.invalid_build_script'),
171
+ help_suggestion: ShopifyCli::Context.message('script.error.build_script_suggestion'),
172
+ }
173
+ when Layers::Infrastructure::Errors::WebAssemblyBinaryNotFoundError
174
+ {
175
+ cause_of_error: ShopifyCli::Context.message('script.error.web_assembly_binary_not_found'),
176
+ help_suggestion: ShopifyCli::Context.message('script.error.web_assembly_binary_not_found_suggestion'),
152
177
  }
153
178
  end
154
179
  end
@@ -1,5 +1,4 @@
1
1
  require 'shopify_cli'
2
- require 'net/http'
3
2
 
4
3
  module ShopifyCli
5
4
  class API
@@ -32,8 +31,9 @@ module ShopifyCli
32
31
  )
33
32
  ctx.debug(resp)
34
33
  resp
35
- rescue API::APIRequestServerError, API::APIRequestUnexpectedError
34
+ rescue API::APIRequestServerError, API::APIRequestUnexpectedError => e
36
35
  ctx.puts(ctx.message('core.api.error.internal_server_error'))
36
+ ctx.debug(ctx.message('core.api.error.internal_server_error_debug', e.message))
37
37
  end
38
38
 
39
39
  protected
@@ -58,14 +58,10 @@ module ShopifyCli
58
58
  unless uri.is_a?(URI::HTTP)
59
59
  ctx.abort("Invalid URL: #{graphql_url}")
60
60
  end
61
- http = ::Net::HTTP.new(uri.host, uri.port)
62
- http.use_ssl = true
63
61
 
64
- req = ::Net::HTTP::Post.new(uri.request_uri)
65
- req.body = JSON.dump(query: body.tr("\n", ""), variables: variables)
66
- req['Content-Type'] = 'application/json'
67
- headers.each { |header, value| req[header] = value }
68
- response = http.request(req)
62
+ # we delay this require so as to avoid a performance hit on starting the CLI
63
+ require 'shopify-cli/http_request'
64
+ response = HttpRequest.call(uri, body, variables, headers)
69
65
 
70
66
  case response.code.to_i
71
67
  when 200..399
@@ -95,7 +91,9 @@ module ShopifyCli
95
91
  def default_headers
96
92
  {
97
93
  'User-Agent' => "Shopify App CLI #{ShopifyCli::VERSION} #{current_sha} | #{ctx.uname}",
98
- }.merge(auth_headers(token))
94
+ }.tap do |headers|
95
+ headers['X-Shopify-Cli-Employee'] = '1' if Shopifolk.acting_as_shopify_organization?
96
+ end.merge(auth_headers(token))
99
97
  end
100
98
 
101
99
  def auth_headers(token)
@@ -6,6 +6,8 @@ module ShopifyCli
6
6
  hidden_feature(feature_set: :debug)
7
7
 
8
8
  subcommand :Feature, 'feature'
9
+ subcommand :Analytics, 'analytics'
10
+ subcommand :ShopifolkBeta, 'shopifolk-beta'
9
11
 
10
12
  def call(*)
11
13
  @ctx.puts(self.class.help)
@@ -16,6 +18,10 @@ module ShopifyCli
16
18
  end
17
19
 
18
20
  class Feature < ShopifyCli::SubCommand
21
+ def self.help
22
+ ShopifyCli::Context.message('core.config.feature.help', ShopifyCli::TOOL_NAME)
23
+ end
24
+
19
25
  options do |parser, flags|
20
26
  parser.on('--enable') { flags[:action] = 'enable' }
21
27
  parser.on('--disable') { flags[:action] = 'disable' }
@@ -28,7 +34,7 @@ module ShopifyCli
28
34
  is_enabled = ShopifyCli::Feature.enabled?(feature_name)
29
35
  if options.flags[:action] == 'disable' && is_enabled
30
36
  ShopifyCli::Feature.disable(feature_name)
31
- @ctx.puts(@ctx.message('core.config.feature.disabled', is_enabled))
37
+ @ctx.puts(@ctx.message('core.config.feature.disabled', feature_name))
32
38
  elsif options.flags[:action] == 'enable' && !is_enabled
33
39
  ShopifyCli::Feature.enable(feature_name)
34
40
  @ctx.puts(@ctx.message('core.config.feature.enabled', feature_name))
@@ -39,6 +45,56 @@ module ShopifyCli
39
45
  end
40
46
  end
41
47
  end
48
+
49
+ class Analytics < ShopifyCli::SubCommand
50
+ def self.help
51
+ ShopifyCli::Context.message('core.config.analytics.help', ShopifyCli::TOOL_NAME)
52
+ end
53
+
54
+ options do |parser, flags|
55
+ parser.on('--enable') { flags[:action] = 'enable' }
56
+ parser.on('--disable') { flags[:action] = 'disable' }
57
+ parser.on('--status') { flags[:action] = 'status' }
58
+ end
59
+
60
+ def call(_args, _name)
61
+ is_enabled = ShopifyCli::Config.get_bool('analytics', 'enabled')
62
+ if options.flags[:action] == 'disable' && is_enabled
63
+ ShopifyCli::Config.set('analytics', 'enabled', false)
64
+ @ctx.puts(@ctx.message('core.config.analytics.disabled'))
65
+ elsif options.flags[:action] == 'enable' && !is_enabled
66
+ ShopifyCli::Config.set('analytics', 'enabled', true)
67
+ @ctx.puts(@ctx.message('core.config.analytics.enabled'))
68
+ elsif is_enabled
69
+ @ctx.puts(@ctx.message('core.config.analytics.is_enabled'))
70
+ else
71
+ @ctx.puts(@ctx.message('core.config.analytics.is_disabled'))
72
+ end
73
+ end
74
+ end
75
+
76
+ class ShopifolkBeta < ShopifyCli::SubCommand
77
+ options do |parser, flags|
78
+ parser.on('--enable') { flags[:action] = 'enable' }
79
+ parser.on('--disable') { flags[:action] = 'disable' }
80
+ parser.on('--status') { flags[:action] = 'status' }
81
+ end
82
+
83
+ def call(_args, _name)
84
+ is_enabled = ShopifyCli::Config.get_bool('shopifolk-beta', 'enabled')
85
+ if options.flags[:action] == 'disable' && is_enabled
86
+ ShopifyCli::Config.set('shopifolk-beta', 'enabled', false)
87
+ @ctx.puts(@ctx.message('core.config.shopifolk_beta.disabled'))
88
+ elsif options.flags[:action] == 'enable' && !is_enabled
89
+ ShopifyCli::Config.set('shopifolk-beta', 'enabled', true)
90
+ @ctx.puts(@ctx.message('core.config.shopifolk_beta.enabled'))
91
+ elsif is_enabled
92
+ @ctx.puts(@ctx.message('core.config.shopifolk_beta.is_enabled'))
93
+ else
94
+ @ctx.puts(@ctx.message('core.config.shopifolk_beta.is_disabled'))
95
+ end
96
+ end
97
+ end
42
98
  end
43
99
  end
44
100
  end
@@ -3,23 +3,33 @@ require 'shopify_cli'
3
3
  module ShopifyCli
4
4
  module Commands
5
5
  class Connect < ShopifyCli::Command
6
- def call(*)
7
- project_type = ask_project_type unless Project.has_current?
8
-
9
- if Project.has_current? && Project.current && Project.current.env
10
- @ctx.puts @ctx.message('core.connect.already_connected_warning')
11
- prod_warning = @ctx.message('core.connect.production_warning')
12
- @ctx.puts prod_warning if [:rails, :node].include?(Project.current_project_type)
6
+ class << self
7
+ def call(args, command_name)
8
+ ProjectType.load_type(args[0]) unless args.empty?
9
+ super
13
10
  end
14
11
 
15
- org = ShopifyCli::Tasks::EnsureEnv.call(@ctx, regenerate: true)
16
- write_cli_yml(project_type, org['id']) unless Project.has_current?
17
- api_key = Project.current(force_reload: true).env['api_key']
18
- @ctx.puts(@ctx.message('core.connect.connected', get_app(org['apps'], api_key).first["title"]))
12
+ def help
13
+ ShopifyCli::Context.message('core.connect.help', ShopifyCli::TOOL_NAME)
14
+ end
19
15
  end
20
16
 
21
- def get_app(apps, api_key)
22
- apps.select { |app| app["apiKey"] == api_key }
17
+ def call(args, command_name)
18
+ if Project.current&.env
19
+ @ctx.puts(@ctx.message('core.connect.already_connected_warning'))
20
+ end
21
+
22
+ project_type = ask_project_type
23
+
24
+ klass = ProjectType.load_type(project_type)&.connect_command
25
+
26
+ if klass
27
+ klass.ctx = @ctx
28
+ klass.call(args, command_name, 'connect')
29
+ else
30
+ app = default_connect(project_type)
31
+ @ctx.done(@ctx.message('core.connect.connected', app))
32
+ end
23
33
  end
24
34
 
25
35
  def ask_project_type
@@ -30,6 +40,13 @@ module ShopifyCli
30
40
  end
31
41
  end
32
42
 
43
+ def default_connect(project_type)
44
+ org = ShopifyCli::Tasks::EnsureEnv.call(@ctx, regenerate: true)
45
+ write_cli_yml(project_type, org['id']) unless Project.has_current?
46
+ api_key = Project.current(force_reload: true).env['api_key']
47
+ get_app(org['apps'], api_key).first['title']
48
+ end
49
+
33
50
  def write_cli_yml(project_type, org_id)
34
51
  ShopifyCli::Project.write(
35
52
  @ctx,
@@ -39,8 +56,8 @@ module ShopifyCli
39
56
  @ctx.done(@ctx.message('core.connect.cli_yml_saved'))
40
57
  end
41
58
 
42
- def self.help
43
- ShopifyCli::Context.message('core.connect.help', ShopifyCli::TOOL_NAME)
59
+ def get_app(apps, api_key)
60
+ apps.select { |app| app["apiKey"] == api_key }
44
61
  end
45
62
  end
46
63
  end
@@ -23,6 +23,7 @@ module ShopifyCli
23
23
  display_cli_ruby(show_all_details)
24
24
  display_utility_commands(show_all_details)
25
25
  display_project_commands(show_all_details)
26
+ display_shopify_staff_identity if show_all_details
26
27
  end
27
28
 
28
29
  def self.help
@@ -139,6 +140,14 @@ module ShopifyCli
139
140
  end
140
141
  @ctx.puts("")
141
142
  end
143
+
144
+ def display_shopify_staff_identity
145
+ is_shopifolk = ShopifyCli::Shopifolk.check
146
+ if is_shopifolk
147
+ @ctx.puts("\n" + @ctx.message('core.system.identity_header'))
148
+ @ctx.puts(" " + @ctx.message('core.system.identity_is_shopifolk'))
149
+ end
150
+ end
142
151
  end
143
152
  end
144
153
  end
@@ -28,7 +28,7 @@ module ShopifyCli
28
28
  ctx.puts(
29
29
  ctx.message('core.warning.development_version', File.join(ShopifyCli::ROOT, 'bin', ShopifyCli::TOOL_NAME))
30
30
  )
31
- else
31
+ elsif !ctx.testing?
32
32
  new_version = ctx.new_version
33
33
  ctx.puts(ctx.message('core.warning.new_version', ShopifyCli::VERSION, new_version)) unless new_version.nil?
34
34
  end
@@ -7,7 +7,7 @@ module ShopifyCli
7
7
  module Core
8
8
  module Monorail
9
9
  ENDPOINT_URI = URI.parse('https://monorail-edge.shopifycloud.com/v1/produce')
10
- INVOCATIONS_SCHEMA = 'app_cli_command/4.0'
10
+ INVOCATIONS_SCHEMA = 'app_cli_command/5.0'
11
11
 
12
12
  # Extra hash of data that will be sent in the payload
13
13
  @metadata = {}
@@ -101,14 +101,16 @@ module ShopifyCli
101
101
  uname: RbConfig::CONFIG["host"],
102
102
  cli_version: ShopifyCli::VERSION,
103
103
  ruby_version: RUBY_VERSION,
104
+ is_employee: ShopifyCli::Shopifolk.acting_as_shopify_organization?,
104
105
  }.tap do |payload|
105
- payload[:metadata] = JSON.dump(metadata) unless metadata.empty?
106
-
106
+ payload[:api_key] = metadata.delete(:api_key)
107
+ payload[:partner_id] = metadata.delete(:organization_id)
107
108
  if Project.has_current?
108
- project = Project.current
109
+ project = Project.current(force_reload: true)
109
110
  payload[:api_key] = project.env&.api_key
110
111
  payload[:partner_id] = project.config['organization_id']
111
112
  end
113
+ payload[:metadata] = JSON.dump(metadata) unless metadata.empty?
112
114
  end,
113
115
  }
114
116
  end
@@ -0,0 +1,15 @@
1
+ require 'net/http'
2
+
3
+ module ShopifyCli
4
+ class HttpRequest
5
+ def self.call(uri, body, variables, headers)
6
+ http = ::Net::HTTP.new(uri.host, uri.port)
7
+ http.use_ssl = true
8
+ req = ::Net::HTTP::Post.new(uri.request_uri)
9
+ req.body = JSON.dump(query: body.tr("\n", ""), variables: variables)
10
+ req['Content-Type'] = 'application/json'
11
+ headers.each { |header, value| req[header] = value }
12
+ http.request(req)
13
+ end
14
+ end
15
+ end
@@ -59,7 +59,7 @@ module ShopifyCli
59
59
  end
60
60
 
61
61
  def npm(verbose = false)
62
- cmd = %w(npm install --no-audit --no-optional)
62
+ cmd = %w(npm install --no-audit)
63
63
  cmd << '--quiet' unless verbose
64
64
 
65
65
  run_install_command(cmd)
@@ -76,23 +76,40 @@ module ShopifyCli
76
76
  # - `ctx`: running context from your command
77
77
  # - `yarn`: The proc, array, or string command to run if yarn is available
78
78
  # - `npm`: The proc, array, or string command to run if npm is available
79
+ # - `capture_response`: The boolean flag to capture the output of the running command if it is set to true
79
80
  #
80
81
  # #### Example
81
82
  #
82
- # ShopifyCli::JsSystem.new(ctx: ctx).call(yarn: ['install', '--silent'], npm: ['install', '--no-audit'])
83
+ # ShopifyCli::JsSystem.new(ctx: ctx).call(
84
+ # yarn: ['install', '--silent'],
85
+ # npm: ['install', '--no-audit'],
86
+ # capture_response: false
87
+ # )
83
88
  #
84
- def call(yarn:, npm:)
85
- yarn? ? call_command(yarn, YARN_CORE_COMMAND) : call_command(npm, NPM_CORE_COMMAND)
89
+ def call(yarn:, npm:, capture_response: false)
90
+ if yarn?
91
+ call_command(yarn, YARN_CORE_COMMAND, capture_response)
92
+ else
93
+ call_command(npm, NPM_CORE_COMMAND, capture_response)
94
+ end
86
95
  end
87
96
 
88
97
  private
89
98
 
90
- def call_command(command, core_command)
99
+ def call_command(command, core_command, capture_response)
91
100
  if command.is_a?(String) || command.is_a?(Array)
92
- CLI::Kit::System.system(core_command, *command, chdir: ctx.root).success?
101
+ capture_response ? call_with_capture(command, core_command) : call_without_capture(command, core_command)
93
102
  else
94
103
  command.call
95
104
  end
96
105
  end
106
+
107
+ def call_with_capture(command, core_command)
108
+ CLI::Kit::System.capture3(core_command, *command, chdir: ctx.root)
109
+ end
110
+
111
+ def call_without_capture(command, core_command)
112
+ CLI::Kit::System.system(core_command, *command, chdir: ctx.root).success?
113
+ end
97
114
  end
98
115
  end