shopify-cli 2.10.2 → 2.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.yaml +117 -0
  3. data/.github/ISSUE_TEMPLATE/enhancement.yaml +38 -0
  4. data/.github/ISSUE_TEMPLATE/feature.yaml +47 -0
  5. data/CHANGELOG.md +20 -3
  6. data/Gemfile.lock +1 -1
  7. data/dev.yml +3 -0
  8. data/lib/project_types/extension/commands/build.rb +3 -0
  9. data/lib/project_types/extension/commands/check.rb +3 -0
  10. data/lib/project_types/extension/commands/create.rb +3 -0
  11. data/lib/project_types/extension/commands/push.rb +3 -0
  12. data/lib/project_types/extension/commands/serve.rb +3 -0
  13. data/lib/project_types/extension/models/specification_handlers/default.rb +1 -1
  14. data/lib/project_types/extension/tasks/convert_server_config.rb +3 -1
  15. data/lib/project_types/script/commands/connect.rb +3 -1
  16. data/lib/project_types/script/commands/create.rb +2 -0
  17. data/lib/project_types/script/commands/push.rb +6 -0
  18. data/lib/project_types/script/layers/infrastructure/errors.rb +4 -3
  19. data/lib/project_types/script/layers/infrastructure/script_service.rb +2 -2
  20. data/lib/project_types/script/loaders/project.rb +2 -1
  21. data/lib/project_types/script/messages/messages.rb +87 -86
  22. data/lib/project_types/script/ui/error_handler.rb +34 -14
  23. data/lib/project_types/theme/commands/check.rb +3 -0
  24. data/lib/project_types/theme/commands/delete.rb +3 -0
  25. data/lib/project_types/theme/commands/init.rb +3 -0
  26. data/lib/project_types/theme/commands/language_server.rb +3 -0
  27. data/lib/project_types/theme/commands/package.rb +3 -0
  28. data/lib/project_types/theme/commands/publish.rb +3 -0
  29. data/lib/project_types/theme/commands/pull.rb +7 -1
  30. data/lib/project_types/theme/commands/push.rb +7 -1
  31. data/lib/project_types/theme/commands/serve.rb +3 -0
  32. data/lib/shopify_cli/command/sub_command.rb +2 -0
  33. data/lib/shopify_cli/command.rb +66 -0
  34. data/lib/shopify_cli/commands/app/create/node.rb +3 -0
  35. data/lib/shopify_cli/commands/app/create/rails.rb +3 -0
  36. data/lib/shopify_cli/commands/app/create.rb +3 -0
  37. data/lib/shopify_cli/commands/app/deploy.rb +3 -0
  38. data/lib/shopify_cli/commands/app/serve.rb +3 -0
  39. data/lib/shopify_cli/constants.rb +12 -0
  40. data/lib/shopify_cli/environment.rb +27 -1
  41. data/lib/shopify_cli/exception_reporter.rb +9 -0
  42. data/lib/shopify_cli/github/issue_url_generator.rb +19 -8
  43. data/lib/shopify_cli/identity_auth/env_auth_token.rb +34 -0
  44. data/lib/shopify_cli/identity_auth.rb +33 -15
  45. data/lib/shopify_cli/messages/messages.rb +1 -1
  46. data/lib/shopify_cli/partners_api.rb +7 -2
  47. data/lib/shopify_cli/services/app/create/rails_service.rb +37 -13
  48. data/lib/shopify_cli/theme/include_filter.rb +39 -17
  49. data/lib/shopify_cli/utilities.rb +7 -0
  50. data/lib/shopify_cli/version.rb +1 -1
  51. data/lib/shopify_cli.rb +1 -0
  52. data/vendor/deps/cli-kit/lib/cli/kit/system.rb +1 -1
  53. data/vendor/deps/cli-kit/lib/cli/kit/util.rb +5 -1
  54. data/vendor/lib/semantic/version.rb +0 -1
  55. metadata +7 -3
  56. data/lib/project_types/rails/commands/create.rb +0 -210
@@ -5,88 +5,91 @@ module Script
5
5
  MESSAGES = {
6
6
  script: {
7
7
  help: <<~HELP,
8
- Suite of commands for developing script applications. See {{command:%1$s script <command> --help}} for usage of each command.
8
+ Suite of commands for developing script applications. Run {{command:%1$s script <command> --help}} for usage of each command.
9
9
  Usage: {{command:%1$s script [ %2$s ]}}
10
10
  HELP
11
11
 
12
12
  error: {
13
- deprecated_ep: "This project uses a Script API (%s) that has been deprecated. "\
14
- "This Script won't work in production.",
15
- deprecated_ep_cause: "Try using a different Script API.",
13
+ deprecated_ep: "This script won't run in a store because "\
14
+ "it uses a deprecated Script API (%s).",
15
+ deprecated_ep_cause: "Recreate this script using a supported Script API.",
16
16
  generic: "{{red:{{x}} Error}}",
17
17
  eacces_cause: "You don't have permission to write to this directory.",
18
- eacces_help: "Try again and choose a different directory.",
18
+ eacces_help: "Get permission for this directory or choose a different one.",
19
19
 
20
20
  enospc_cause: "You don't have enough disk space to do this action.",
21
- enospc_help: "Free up some space and try again.",
21
+ enospc_help: "Free up more space.",
22
22
 
23
23
  oauth_cause: "Something went wrong while authenticating your account with the Partner Dashboard.",
24
- oauth_help: "Try again.",
24
+ oauth_help: "Wait a few minutes and try again.",
25
25
 
26
- invalid_context_cause: "Your .shopify-cli.yml file is not correct. Values are missing for "\
26
+ invalid_context_cause: "Your .shopify-cli.yml is formatted incorrectly. It's missing values for "\
27
27
  "extension_point_type or script_name.",
28
- invalid_context_help: "Add these values and try again.",
28
+ invalid_context_help: "Add these values.",
29
29
 
30
- invalid_script_name_cause: "Invalid script name.",
31
- invalid_script_name_help: "Replace or remove unsupported characters. Valid characters "\
32
- "are numbers, letters, hyphens, or underscores.",
30
+ invalid_script_name_cause: "Script name contains unsupported characters.",
31
+ invalid_script_name_help: "Use only numbers, letters, hyphens, or underscores.",
33
32
 
34
- no_existing_apps_cause: "You don't have any apps in your Partner Dashboard.",
35
- no_existing_apps_help: "Create an app with {{command:shopify [node|rails] create}}" \
36
- " or visit https://partners.shopify.com/.",
33
+ no_existing_apps_cause: "Your script can't be pushed to an app because your Partner account "\
34
+ "doesn't have any apps.",
35
+ no_existing_apps_help: "Create an app.",
37
36
 
38
- no_existing_orgs_cause: "You don't have any partner organizations.",
39
- no_existing_orgs_help: "Visit https://partners.shopify.com/ to create a partners account.",
37
+ no_existing_orgs_cause: "Your account doesn't belong to a Partner Organization.",
38
+ no_existing_orgs_help: "Visit https://partners.shopify.com/ to create an account.",
40
39
 
41
40
  project_exists_cause: "A directory with this same name already exists.",
42
- project_exists_help: "Try again and enter a different name for the script.",
41
+ project_exists_help: "Choose a different name for your script.",
43
42
 
44
- invalid_extension_cause: "Invalid Script API %s.",
45
- invalid_extension_help: "Allowed values: %s.",
43
+ invalid_extension_cause: "The name of the Script API is incorrect: %s.",
44
+ invalid_extension_help: "Choose a supported API: %s.",
46
45
 
47
- invalid_language_cause: "Invalid language %s.",
48
- invalid_language_help: "Allowed values: %s.",
46
+ invalid_language_cause: "The language is not supported: %s.",
47
+ invalid_language_help: "Choose a supported language: %s.",
49
48
 
50
49
  missing_script_config_field_cause: "The %{filename} file is missing the required %{field} field.",
51
- missing_script_config_field_help: "Add the field and try again.",
50
+ missing_script_config_field_help: "Add the field.",
52
51
 
53
- script_config_parse_error_cause: "The %{filename} file contains invalid %{serialization_format}.",
54
- script_config_parse_error_help: "Fix the errors and try again.",
52
+ script_config_parse_error_cause: "The %{filename} file contains incorrect %{serialization_format}.",
53
+ script_config_parse_error_help: "Correct the errors.",
55
54
 
56
55
  no_script_config_file_cause: "The %{filename} file is missing.",
57
- no_script_config_file_help: "Create this file and try again.",
56
+ no_script_config_file_help: "Create this file.",
58
57
 
59
- app_not_connected_cause: "Script is not connected to an app.",
60
- app_not_connected_help: "Run shopify connect or enter fields for api-key and api-secret.",
58
+ app_not_connected_cause: "The script is not connected to an app.",
59
+ app_not_connected_help: "Run {{command:%{tool_name} script connect}}.",
61
60
 
62
- configuration_definition_error_cause: "In the %{filename} file, there was a problem with the "\
61
+ configuration_definition_error_cause: "In %{filename} there is a problem with the "\
63
62
  "configuration. %{message}",
64
- configuration_definition_error_help: "Fix the error and try again.",
63
+ configuration_definition_error_help: "Fix the error.",
65
64
 
66
- configuration_syntax_error_cause: "The %{filename} is not formatted properly.",
67
- configuration_syntax_error_help: "Fix the errors and try again.",
65
+ configuration_definition_errors_cause: "In %{filename}, there are %{error_count} problems with "\
66
+ "the configuration:\n%{concatenated_messages}\n",
67
+ configuration_definition_errors_help: "Correct the errors.",
68
68
 
69
- configuration_missing_keys_error_cause: "The %{filename} file is missing required keys: "\
69
+ configuration_syntax_error_cause: "The %{filename} is not formatted correctly.",
70
+ configuration_syntax_error_help: "Fix the errors.",
71
+
72
+ configuration_missing_keys_error_cause: "The %{filename} is missing required keys: "\
70
73
  "%{missing_keys}.",
71
- configuration_missing_keys_error_help: "Add the keys and try again.",
74
+ configuration_missing_keys_error_help: "Add the keys.",
72
75
 
73
- configuration_invalid_value_error_cause: "The %{filename} configuration only accepts "\
76
+ configuration_invalid_value_error_cause: "The %{filename} configuration accepts "\
74
77
  "one of the following types(s): %{valid_input_modes}.",
75
- configuration_invalid_value_error_help: "Change the type and try again.",
78
+ configuration_invalid_value_error_help: "Change the value of the type.",
76
79
 
77
80
  configuration_schema_field_missing_keys_error_cause: "A configuration entry in the %{filename} file "\
78
81
  "is missing required keys: %{missing_keys}.",
79
- configuration_definition_schema_field_missing_keys_error_help: "Add the keys and try again.",
82
+ configuration_definition_schema_field_missing_keys_error_help: "Add the keys.",
80
83
 
81
84
  configuration_schema_field_invalid_value_error_cause: "The configuration entries in the "\
82
- "%{filename} file only accept one of the following "\
85
+ "%{filename} file accept one of the following "\
83
86
  "type(s): %{valid_types}.",
84
- configuration_schema_field_invalid_value_error_help: "Change the types and try again.",
87
+ configuration_schema_field_invalid_value_error_help: "Change the value of the type.",
85
88
 
86
- script_not_found_cause: "Couldn't find a script %s for the Script API %s",
89
+ script_not_found_cause: "Can't find script %s for Script API %s",
87
90
 
88
- system_call_failure_cause: "An error was returned while running {{command:%{cmd}}}.",
89
- system_call_failure_help: "Review the following error and try again.\n{{red:%{out}}}",
91
+ system_call_failure_cause: "Something went wrong while running: {{command:%{cmd}}}.",
92
+ system_call_failure_help: "Correct the error.\n{{red:%{out}}}",
90
93
 
91
94
  metadata_validation_cause: "The Script API metadata is incorrect.",
92
95
  metadata_validation_help: "The 'schemaVersions.major' field contains an unsupported version.",
@@ -100,60 +103,58 @@ module Script
100
103
  metadata_schema_versions_missing_minor: "Invalid Script API metadata:" \
101
104
  " 'schemaVersions' is missing the 'minor' field",
102
105
 
103
- metadata_not_found_cause: "Script version file (%s) cannot be found.",
104
- metadata_not_found_help: "Ensure the 'shopify/scripts-toolchain-as' package is up to date and " \
105
- "'package.json' contains a 'scripts/build' entry with a " \
106
- "'--metadata build/metadata.json' argument",
106
+ metadata_not_found_cause: "Can't find the script version file (%{filename}).",
107
+ metadata_not_found_help: "Make sure your project is up-to-date and a script metadata file " \
108
+ "is accessible at %{filename}.",
107
109
 
108
110
  build_error_cause: "Something went wrong while building the script.",
109
- build_error_help: "Correct the errors and try again.",
111
+ build_error_help: "Correct the errors.",
110
112
 
111
113
  dependency_install_cause: "Something went wrong while installing the needed dependencies.",
112
- dependency_install_help: "Correct the errors and try again.",
114
+ dependency_install_help: "Correct the errors.",
113
115
 
114
116
  failed_api_request_cause: "Something went wrong while communicating with Shopify.",
115
117
  failed_api_request_help: "Try again.",
116
118
 
117
- forbidden_error_cause: "You do not have permission to do this action.",
119
+ forbidden_error_cause: "You don't have permission to do this action.",
118
120
 
119
121
  graphql_error_cause: "An error was returned: %s.",
120
- graphql_error_help: "\nReview the error and try again.",
122
+ graphql_error_help: "\nCorrect the error.",
121
123
 
122
- script_repush_cause: "A version of this script already exists on the app.",
124
+ script_repush_cause: "Can’t push the script because a version of this script already exists on the app.",
123
125
  script_repush_help: "Use {{cyan:--force}} to replace the existing script.",
124
126
 
125
127
  build_script_not_found: "The root package.json is missing the build command that " \
126
- "is needed to compile your script to WebAssembly.",
128
+ "is needed to compile your script to Wasm.",
127
129
  # rubocop:disable Layout/LineLength
128
130
  build_script_suggestion: "\n\nFor example, your package.json needs the following command:" \
129
131
  "\nbuild: npx shopify-scripts-toolchain-as build --src src/shopify_main.ts --binary build/<script_name>.wasm --metadata build/metadata.json -- --lib node_modules --optimize --use Date=",
130
132
 
131
- web_assembly_binary_not_found: "WebAssembly binary not found.",
132
- web_assembly_binary_not_found_suggestion: "No WebAssembly binary found. " \
133
- "Check that your build script outputs the generated binary to the root of the directory. " \
134
- "Generated binary should match the script name: <script_name>.wasm",
133
+ web_assembly_binary_not_found: "Wasm binary not found.",
134
+ web_assembly_binary_not_found_suggestion: "Check that there is a valid Wasm binary in the root directory" \
135
+ "Your Wasm binary should match the script name: <script_name>.wasm",
135
136
 
136
137
  project_config_not_found: "Internal error - Script can't be created because the project's config file is missing from the repository.",
137
138
 
138
139
  invalid_project_config: "Internal error - Script can't be created because the project's config file is invalid in the repository.",
139
140
 
140
- script_upload_cause: "Fail to upload script.",
141
+ script_upload_cause: "Something went wrong and your script couldn't be pushed.",
141
142
  script_upload_help: "Try again.",
142
143
 
143
144
  script_too_large_cause: "The size of your Wasm binary file is too large.",
144
145
  script_too_large_help: "It must be less than %{max_size}.",
145
146
 
146
147
  api_library_not_found_cause: "Script can't be created because API library %{library_name} is missing from the dependencies",
147
- api_library_not_found_help: "This error can occur because the API library was removed from your system or there is a problem with dependencies in the repository.",
148
+ api_library_not_found_help: "This can occur because the API library was removed from your system or there is a problem with dependencies in the repository.",
148
149
 
149
150
  language_library_for_api_not_found_cause: "Script can’t be pushed because the %{language} library for API %{api} is missing.",
150
151
  language_library_for_api_not_found_help: "Make sure extension_point.yml contains the correct API library.",
151
- no_scripts_found_in_app: "The selected apps have no scripts. Please, create them first on the partners' dashboard.",
152
+ no_scripts_found_in_app: "The selected apps have no scripts. Create them first on the partners' dashboard.",
152
153
  missing_env_file_variables: "The following variables are missing in the .env file: %s."\
153
- " It might happen when the script hasn't been previously connected to an app."\
154
+ " This can occur when the script hasn't been connected to an app."\
154
155
  " To connect the script to an app, run {{command:%s script connect}}",
155
- missing_push_options: "The following options are required: %s."\
156
- " You can obtain them from the .env file generated after connecting the script to an app.",
156
+ missing_push_options: "The following options are missing from .env: %s."\
157
+ " Run {{command:%s script connect}} to connect the script to an app and generate these options.",
157
158
  },
158
159
 
159
160
  create: {
@@ -161,29 +162,29 @@ module Script
161
162
  {{command:%1$s script create}}: Creates a script project.
162
163
  Usage: {{command:%1$s script create}}
163
164
  Options:
164
- {{command:--name=NAME}} Script project name. Use any string.
165
- {{command:--api=TYPE}} Script API name. Allowed values: %2$s.
166
- {{command:--language=LANGUAGE}} Programming language. Allowed values: %3$s.
165
+ {{command:--name=NAME}} Script project name.
166
+ {{command:--api=TYPE}} Script API name. Supported values: %2$s.
167
+ {{command:--language=LANGUAGE}} Programming language. Supported values: %3$s.
167
168
  HELP
168
169
 
169
170
  error: {
170
- operation_failed: "Script not created.",
171
+ operation_failed: "Something went wrong and the script wasn't created.",
171
172
  },
172
173
 
173
- change_directory_notice: "{{*}} Change directories to {{green:%s}} to run script commands",
174
- creating: "Creating script",
175
- created: "Created script",
176
- preparing_project: "Preparing script project structure",
177
- creating_wasm: "Creating configuration files",
178
- created_wasm: "Configuration files created",
174
+ change_directory_notice: "{{*}} Change directories to {{green:%s}} to run script commands.",
175
+ creating: "Creating script.",
176
+ created: "Created script.",
177
+ preparing_project: "Preparing script project structure.",
178
+ creating_wasm: "Creating configuration files.",
179
+ created_wasm: "Configuration files created.",
179
180
  },
180
181
 
181
182
  push: {
182
183
  help: <<~HELP,
183
- Build the script, upload it to Shopify, and register it to an app. If you've already pushed the script to this app, then use --force to replace the existing version on the app.
184
+ Build the script, upload it to Shopify, and register it to an app.
184
185
  Usage: {{command:%s script push}}
185
186
  Options:
186
- {{command:[--force]}} Replaces the existing script on the app with this version.
187
+ {{command:[--force]}} Replace the existing script with this version.
187
188
  {{command:[--api-key=API_KEY]}} The API key used to register an app with the script. This can be found on the app page on Partners Dashboard. Overrides the value in the .env file, if present.
188
189
  {{command:[--api-secret=API_SECRET]}} The API secret of the app the script is registered with. Overrides the value in the .env file, if present.
189
190
  {{command:[--uuid=UUID]}} The uuid of the script. Overrides the value in the .env file, if present.
@@ -206,34 +207,34 @@ module Script
206
207
  error: {
207
208
  operation_failed: "Couldn't connect script to app.",
208
209
  missing_env_file_variables: "The following variables are missing in the .env file: %s."\
209
- " To connect the script to an app, either enter the value into the .env file or delete the .env file, then run {{command:%s script connect}}",
210
+ " To connect the script to an app, enter the value into the .env file or delete the .env file, and then run {{command:%s script connect}}",
210
211
  },
211
212
  },
212
213
  javy: {
213
214
  help: <<~HELP,
214
- Compile the JavaScript code into WebAssembly.
215
+ Compile the JavaScript code into Wasm.
215
216
  Usage: {{command:%s script javy}}
216
217
  Options:
217
218
  {{command:--in}} The name of the JavaScript file that will be compiled.
218
- {{command:--out}} The name of the file that the WebAssembly should be written to.
219
+ {{command:--out}} The name of the file that the Wasm should be written to.
219
220
  HELP
220
221
  errors: {
221
- invalid_arguments: "Javy was run with invalid arguments. Run {{command: %s script javy --help}} for more information.",
222
+ invalid_arguments: "Javy was run with invalid arguments. Run {{command: %s script javy --help}}.",
222
223
  },
223
224
  },
224
225
 
225
226
  project_deps: {
226
- none_required: "{{v}} None required",
227
- checking: "Checking dependencies",
228
- installing: "Dependencies installing",
229
- installed: "Missing dependencies installed",
227
+ none_required: "{{v}} Dependencies are up to date.",
228
+ checking: "Checking dependencies.",
229
+ installing: "Installing dependencies.",
230
+ installed: "Installed missing dependencies.",
230
231
  },
231
232
 
232
233
  forms: {
233
234
  create: {
234
235
  select_extension_point: "Which Script API do you want to use?",
235
236
  select_language: "Which language do you want to use?",
236
- script_name: "Script name",
237
+ script_name: "What do you want to name your script?",
237
238
  },
238
239
  },
239
240
 
@@ -247,10 +248,10 @@ module Script
247
248
  ensure_env: {
248
249
  organization: "Partner organization {{green:%s (%s)}}.",
249
250
  organization_select: "Which partner organization do you want to use?",
250
- app: "Script will be pushed to app {{green:%s}}.",
251
+ app: "Push script to app {{green:%s}}.",
251
252
  app_select: "Which app do you want to push this script to?",
252
- ask_connect_to_existing_script: "The selected app has some scripts. Do you want to replace any of the "\
253
- "existing scripts on the app with this script?",
253
+ ask_connect_to_existing_script: "This app contains scripts. Do you want to replace an "\
254
+ "existing script on the app with this script?",
254
255
  ask_which_script_to_connect_to: "Which script do you want to replace?",
255
256
  },
256
257
  },
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "cli/ui"
2
4
 
3
5
  module Script
@@ -5,9 +7,9 @@ module Script
5
7
  module ErrorHandler
6
8
  def self.display(failed_op:, cause_of_error:, help_suggestion:)
7
9
  $stderr.puts(CLI::UI.fmt(ShopifyCLI::Context.message("script.error.generic")))
8
- full_msg = failed_op ? failed_op.dup : ""
9
- full_msg << " #{cause_of_error}" if cause_of_error
10
- full_msg << " #{help_suggestion}" if help_suggestion
10
+ full_msg = failed_op ? failed_op.dup : String.new
11
+ append_msg(full_msg, cause_of_error) if cause_of_error
12
+ append_msg(full_msg, help_suggestion) if help_suggestion
11
13
  $stderr.puts(CLI::UI.fmt(full_msg.strip))
12
14
  end
13
15
 
@@ -22,6 +24,11 @@ module Script
22
24
  display_and_raise(failed_op: failed_op, **messages)
23
25
  end
24
26
 
27
+ private_class_method def self.append_msg(full_msg, msg_to_append)
28
+ full_msg << " " unless /\s$/.match?(full_msg)
29
+ full_msg << msg_to_append
30
+ end
31
+
25
32
  def self.error_messages(e)
26
33
  case e
27
34
  when Errno::EACCES
@@ -100,8 +107,8 @@ module Script
100
107
  }
101
108
  when Layers::Domain::Errors::MetadataNotFoundError
102
109
  {
103
- cause_of_error: ShopifyCLI::Context.message("script.error.metadata_not_found_cause", e.filename),
104
- help_suggestion: ShopifyCLI::Context.message("script.error.metadata_not_found_help"),
110
+ cause_of_error: ShopifyCLI::Context.message("script.error.metadata_not_found_cause", filename: e.filename),
111
+ help_suggestion: ShopifyCLI::Context.message("script.error.metadata_not_found_help", filename: e.filename),
105
112
  }
106
113
  when Layers::Domain::Errors::MissingScriptConfigFieldError
107
114
  {
@@ -135,14 +142,26 @@ module Script
135
142
  help_suggestion: ShopifyCLI::Context.message("script.error.no_script_config_file_help"),
136
143
  }
137
144
  when Layers::Infrastructure::Errors::ScriptConfigurationDefinitionError
138
- {
139
- cause_of_error: ShopifyCLI::Context.message(
140
- "script.error.configuration_definition_error_cause",
141
- message: e.message,
142
- filename: e.filename,
143
- ),
144
- help_suggestion: ShopifyCLI::Context.message("script.error.configuration_definition_error_help"),
145
- }
145
+ if e.messages.count == 1
146
+ {
147
+ cause_of_error: ShopifyCLI::Context.message(
148
+ "script.error.configuration_definition_error_cause",
149
+ message: e.messages.fetch(0),
150
+ filename: e.filename,
151
+ ),
152
+ help_suggestion: ShopifyCLI::Context.message("script.error.configuration_definition_error_help"),
153
+ }
154
+ else
155
+ {
156
+ cause_of_error: ShopifyCLI::Context.message(
157
+ "script.error.configuration_definition_errors_cause",
158
+ concatenated_messages: e.messages.map { |m| "{{x}} #{m}" }.join("\n"),
159
+ filename: e.filename,
160
+ error_count: e.messages.count,
161
+ ),
162
+ help_suggestion: ShopifyCLI::Context.message("script.error.configuration_definition_errors_help"),
163
+ }
164
+ end
146
165
  when Layers::Infrastructure::Errors::ScriptConfigSyntaxError
147
166
  {
148
167
  cause_of_error: ShopifyCLI::Context.message(
@@ -154,7 +173,8 @@ module Script
154
173
  when Layers::Infrastructure::Errors::ScriptEnvAppNotConnectedError
155
174
  {
156
175
  cause_of_error: ShopifyCLI::Context.message("script.error.app_not_connected_cause"),
157
- help_suggestion: ShopifyCLI::Context.message("script.error.app_not_connected_help"),
176
+ help_suggestion: ShopifyCLI::Context.message("script.error.app_not_connected_help",
177
+ tool_name: ShopifyCLI::TOOL_NAME),
158
178
  }
159
179
  when Layers::Infrastructure::Errors::ScriptConfigMissingKeysError
160
180
  {
@@ -4,6 +4,9 @@ require "theme_check"
4
4
  module Theme
5
5
  class Command
6
6
  class Check < ShopifyCLI::Command::SubCommand
7
+ recommend_default_node_range
8
+ recommend_default_ruby_range
9
+
7
10
  class Options < ShopifyCLI::Options
8
11
  def initialize(theme_check)
9
12
  super()
@@ -5,6 +5,9 @@ require "shopify_cli/theme/development_theme"
5
5
  module Theme
6
6
  class Command
7
7
  class Delete < ShopifyCLI::Command::SubCommand
8
+ recommend_default_node_range
9
+ recommend_default_ruby_range
10
+
8
11
  options do |parser, flags|
9
12
  parser.on("-d", "--development") { flags[:development] = true }
10
13
  parser.on("-a", "--show-all") { flags[:show_all] = true }
@@ -3,6 +3,9 @@
3
3
  module Theme
4
4
  class Command
5
5
  class Init < ShopifyCLI::Command::SubCommand
6
+ recommend_default_node_range
7
+ recommend_default_ruby_range
8
+
6
9
  options do |parser, flags|
7
10
  parser.on("-u", "--clone-url URL") { |url| flags[:clone_url] = url }
8
11
  end
@@ -4,6 +4,9 @@ require "theme_check"
4
4
  module Theme
5
5
  class Command
6
6
  class LanguageServer < ShopifyCLI::Command::SubCommand
7
+ recommend_default_node_range
8
+ recommend_default_ruby_range
9
+
7
10
  def call(*)
8
11
  ThemeCheck::LanguageServer.start
9
12
  end
@@ -5,6 +5,9 @@ require "json"
5
5
  module Theme
6
6
  class Command
7
7
  class Package < ShopifyCLI::Command::SubCommand
8
+ recommend_default_node_range
9
+ recommend_default_ruby_range
10
+
8
11
  THEME_DIRECTORIES = %w[
9
12
  assets
10
13
  config
@@ -4,6 +4,9 @@ require "shopify_cli/theme/theme"
4
4
  module Theme
5
5
  class Command
6
6
  class Publish < ShopifyCLI::Command::SubCommand
7
+ recommend_default_node_range
8
+ recommend_default_ruby_range
9
+
7
10
  options do |parser, flags|
8
11
  parser.on("-f", "--force") { flags[:force] = true }
9
12
  end
@@ -7,13 +7,19 @@ require "shopify_cli/theme/syncer"
7
7
  module Theme
8
8
  class Command
9
9
  class Pull < ShopifyCLI::Command::SubCommand
10
+ recommend_default_node_range
11
+ recommend_default_ruby_range
12
+
10
13
  options do |parser, flags|
11
14
  parser.on("-n", "--nodelete") { flags[:nodelete] = true }
12
15
  parser.on("-i", "--themeid=ID") { |theme_id| flags[:theme_id] = theme_id }
13
16
  parser.on("-t", "--theme=NAME_OR_ID") { |theme| flags[:theme] = theme }
14
17
  parser.on("-l", "--live") { flags[:live] = true }
15
18
  parser.on("-d", "--development") { flags[:development] = true }
16
- parser.on("-o", "--only=PATTERN") { |pattern| flags[:includes] = pattern }
19
+ parser.on("-o", "--only=PATTERN") do |pattern|
20
+ flags[:includes] ||= []
21
+ flags[:includes] << pattern
22
+ end
17
23
  parser.on("-x", "--ignore=PATTERN") do |pattern|
18
24
  flags[:ignores] ||= []
19
25
  flags[:ignores] << pattern
@@ -8,6 +8,9 @@ require "shopify_cli/theme/syncer"
8
8
  module Theme
9
9
  class Command
10
10
  class Push < ShopifyCLI::Command::SubCommand
11
+ recommend_default_node_range
12
+ recommend_default_ruby_range
13
+
11
14
  options do |parser, flags|
12
15
  parser.on("-n", "--nodelete") { flags[:nodelete] = true }
13
16
  parser.on("-i", "--themeid=ID") { |theme_id| flags[:theme_id] = theme_id }
@@ -18,7 +21,10 @@ module Theme
18
21
  parser.on("-j", "--json") { flags[:json] = true }
19
22
  parser.on("-a", "--allow-live") { flags[:allow_live] = true }
20
23
  parser.on("-p", "--publish") { flags[:publish] = true }
21
- parser.on("-o", "--only=PATTERN") { |pattern| flags[:includes] = pattern }
24
+ parser.on("-o", "--only=PATTERN") do |pattern|
25
+ flags[:includes] ||= []
26
+ flags[:includes] << pattern
27
+ end
22
28
  parser.on("-x", "--ignore=PATTERN") do |pattern|
23
29
  flags[:ignores] ||= []
24
30
  flags[:ignores] << pattern
@@ -4,6 +4,9 @@ require "shopify_cli/theme/dev_server"
4
4
  module Theme
5
5
  class Command
6
6
  class Serve < ShopifyCLI::Command::SubCommand
7
+ recommend_default_node_range
8
+ recommend_default_ruby_range
9
+
7
10
  DEFAULT_HTTP_HOST = "127.0.0.1"
8
11
 
9
12
  options do |parser, flags|
@@ -9,6 +9,8 @@ module ShopifyCLI
9
9
  cmd = new(@ctx)
10
10
  args = cmd.options.parse(@_options, args || [])
11
11
  return call_help(parent_command, command_name) if cmd.options.help
12
+ check_ruby_version
13
+ check_node_version
12
14
  run_prerequisites
13
15
 
14
16
  cmd.call(args, command_name)
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require "shopify_cli"
3
+ require "semantic/semantic"
3
4
 
4
5
  module ShopifyCLI
5
6
  class Command < CLI::Kit::BaseCommand
@@ -7,6 +8,8 @@ module ShopifyCLI
7
8
  autoload :AppSubCommand, "shopify_cli/command/app_sub_command"
8
9
  autoload :ProjectCommand, "shopify_cli/command/project_command"
9
10
 
11
+ VersionRange = Struct.new(:from, :to, keyword_init: true)
12
+
10
13
  extend Feature::Set
11
14
 
12
15
  attr_writer :ctx
@@ -26,6 +29,8 @@ module ShopifyCLI
26
29
  cmd = new(@ctx)
27
30
  cmd.options.parse(@_options, args)
28
31
  return call_help(command_name) if cmd.options.help
32
+ check_ruby_version
33
+ check_node_version
29
34
  run_prerequisites
30
35
  cmd.call(args, command_name)
31
36
  end
@@ -58,6 +63,67 @@ module ShopifyCLI
58
63
  )
59
64
  end
60
65
 
66
+ def recommend_ruby(from:, to:)
67
+ @compatible_ruby_range = VersionRange.new(
68
+ from: Semantic::Version.new(from),
69
+ to: Semantic::Version.new(to)
70
+ )
71
+ end
72
+
73
+ def recommend_default_ruby_range
74
+ recommend_ruby(
75
+ from: Constants::SupportedVersions::Ruby::FROM,
76
+ to: Constants::SupportedVersions::Ruby::TO
77
+ )
78
+ end
79
+
80
+ def check_ruby_version
81
+ check_version(
82
+ Environment.ruby_version,
83
+ range: @compatible_ruby_range,
84
+ runtime: "Ruby"
85
+ )
86
+ end
87
+
88
+ def recommend_node(from:, to:)
89
+ @compatible_node_range = VersionRange.new(
90
+ from: Semantic::Version.new(from),
91
+ to: Semantic::Version.new(to)
92
+ )
93
+ end
94
+
95
+ def recommend_default_node_range
96
+ recommend_node(
97
+ from: Constants::SupportedVersions::Node::FROM,
98
+ to: Constants::SupportedVersions::Node::TO
99
+ )
100
+ end
101
+
102
+ def check_node_version
103
+ check_version(
104
+ Environment.node_version,
105
+ range: @compatible_node_range,
106
+ runtime: "Node"
107
+ )
108
+ end
109
+
110
+ def check_version(version, range:, runtime:)
111
+ return if Environment.test?
112
+ return if range.nil?
113
+
114
+ version_without_pre_nor_build = Utilities.version_dropping_pre_and_build(version)
115
+ is_higher_than_bottom = version_without_pre_nor_build >= Utilities.version_dropping_pre_and_build(range.from)
116
+ is_lower_than_top = version_without_pre_nor_build < Utilities.version_dropping_pre_and_build(range.to)
117
+ return if is_higher_than_bottom && is_lower_than_top
118
+
119
+ Context.new.warn("Your environment #{runtime} version, #{version},"\
120
+ " is outside of the range supported by the CLI,"\
121
+ " #{range.from}..<#{range.to},"\
122
+ " and might cause incompatibility issues.")
123
+ rescue StandardError => error
124
+ ExceptionReporter.report_error_silently(error)
125
+ end
126
+
61
127
  def prerequisite_task(*tasks_without_args, **tasks_with_args)
62
128
  @prerequisite_tasks ||= []
63
129
  @prerequisite_tasks += tasks_without_args.map { |t| PrerequisiteTask.new(t) }
@@ -5,6 +5,9 @@ module ShopifyCLI
5
5
  class Node < ShopifyCLI::Command::AppSubCommand
6
6
  prerequisite_task :ensure_authenticated
7
7
 
8
+ recommend_default_node_range
9
+ recommend_default_ruby_range
10
+
8
11
  options do |parser, flags|
9
12
  parser.on("--name=NAME") { |t| flags[:name] = t }
10
13
  parser.on("--organization-id=ID") { |id| flags[:organization_id] = id }