shopify-cli 2.7.3 → 2.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +44 -0
  4. data/Gemfile.lock +1 -1
  5. data/RELEASING.md +4 -3
  6. data/dev.yml +2 -2
  7. data/ext/javy/javy.rb +8 -9
  8. data/lib/graphql/get_extension_registrations.graphql +27 -0
  9. data/lib/project_types/extension/cli.rb +27 -2
  10. data/lib/project_types/extension/commands/build.rb +10 -10
  11. data/lib/project_types/extension/commands/create.rb +2 -3
  12. data/lib/project_types/extension/commands/push.rb +36 -8
  13. data/lib/project_types/extension/extension_project.rb +1 -1
  14. data/lib/project_types/extension/features/argo_serve.rb +6 -5
  15. data/lib/project_types/extension/forms/questions/ask_registration.rb +6 -2
  16. data/lib/project_types/extension/loaders/project.rb +29 -0
  17. data/lib/project_types/extension/loaders/specification_handler.rb +22 -0
  18. data/lib/project_types/extension/messages/messages.rb +4 -0
  19. data/lib/project_types/extension/models/app.rb +1 -1
  20. data/lib/project_types/extension/models/development_server.rb +2 -4
  21. data/lib/project_types/extension/models/specification_handlers/default.rb +4 -0
  22. data/lib/project_types/extension/tasks/convert_server_config.rb +3 -1
  23. data/lib/project_types/extension/tasks/execute_commands/base.rb +13 -0
  24. data/lib/project_types/extension/tasks/execute_commands/build.rb +29 -0
  25. data/lib/project_types/extension/tasks/execute_commands/create.rb +33 -0
  26. data/lib/project_types/extension/tasks/execute_commands/serve.rb +35 -0
  27. data/lib/project_types/extension/tasks/merge_server_config.rb +33 -22
  28. data/lib/project_types/rails/gem.rb +1 -2
  29. data/lib/project_types/script/cli.rb +7 -0
  30. data/lib/project_types/script/commands/connect.rb +19 -0
  31. data/lib/project_types/script/commands/create.rb +8 -2
  32. data/lib/project_types/script/commands/push.rb +35 -12
  33. data/lib/project_types/script/layers/application/connect_app.rb +15 -3
  34. data/lib/project_types/script/layers/application/create_script.rb +16 -16
  35. data/lib/project_types/script/layers/application/extension_points.rb +50 -26
  36. data/lib/project_types/script/layers/application/push_script.rb +5 -2
  37. data/lib/project_types/script/layers/domain/errors.rb +3 -2
  38. data/lib/project_types/script/layers/domain/extension_point.rb +14 -0
  39. data/lib/project_types/script/layers/domain/script_config.rb +6 -4
  40. data/lib/project_types/script/layers/infrastructure/errors.rb +38 -23
  41. data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +49 -28
  42. data/lib/project_types/script/layers/infrastructure/script_service.rb +22 -5
  43. data/lib/project_types/script/loaders/project.rb +44 -0
  44. data/lib/project_types/script/loaders/specification_handler.rb +22 -0
  45. data/lib/project_types/script/messages/messages.rb +39 -16
  46. data/lib/project_types/script/ui/error_handler.rb +46 -29
  47. data/lib/project_types/theme/commands/pull.rb +45 -17
  48. data/lib/project_types/theme/commands/push.rb +65 -28
  49. data/lib/project_types/theme/commands/serve.rb +5 -0
  50. data/lib/project_types/theme/messages/messages.rb +34 -18
  51. data/lib/shopify_cli/command.rb +6 -0
  52. data/lib/shopify_cli/commands/login.rb +1 -1
  53. data/lib/shopify_cli/commands/switch.rb +1 -1
  54. data/lib/shopify_cli/constants.rb +11 -2
  55. data/lib/shopify_cli/context.rb +66 -12
  56. data/lib/shopify_cli/core/executor.rb +4 -4
  57. data/lib/shopify_cli/environment.rb +50 -20
  58. data/lib/shopify_cli/form.rb +2 -0
  59. data/lib/shopify_cli/identity_auth.rb +4 -3
  60. data/lib/shopify_cli/messages/messages.rb +9 -1
  61. data/lib/shopify_cli/method_object.rb +21 -9
  62. data/lib/shopify_cli/partners_api/app_extensions/job.rb +36 -0
  63. data/lib/shopify_cli/partners_api/app_extensions.rb +46 -0
  64. data/lib/shopify_cli/partners_api/organizations.rb +2 -5
  65. data/lib/shopify_cli/partners_api.rb +1 -0
  66. data/lib/shopify_cli/project.rb +8 -7
  67. data/lib/shopify_cli/resources/env_file.rb +18 -6
  68. data/lib/shopify_cli/result.rb +61 -59
  69. data/lib/shopify_cli/task.rb +5 -3
  70. data/lib/shopify_cli/theme/dev_server/cdn/cdn_helper.rb +49 -0
  71. data/lib/shopify_cli/theme/dev_server/cdn_assets.rb +69 -0
  72. data/lib/shopify_cli/theme/dev_server/cdn_fonts.rb +8 -28
  73. data/lib/shopify_cli/theme/dev_server/hot-reload.js +34 -3
  74. data/lib/shopify_cli/theme/dev_server/hot_reload.rb +18 -2
  75. data/lib/shopify_cli/theme/dev_server/local_assets.rb +4 -0
  76. data/lib/shopify_cli/theme/dev_server/proxy/template_param_builder.rb +84 -0
  77. data/lib/shopify_cli/theme/dev_server/proxy.rb +10 -15
  78. data/lib/shopify_cli/theme/dev_server/reload_mode.rb +34 -0
  79. data/lib/shopify_cli/theme/dev_server.rb +8 -21
  80. data/lib/shopify_cli/theme/file.rb +2 -2
  81. data/lib/shopify_cli/theme/filter/path_matcher.rb +38 -0
  82. data/lib/shopify_cli/theme/ignore_filter.rb +14 -18
  83. data/lib/shopify_cli/theme/include_filter.rb +43 -0
  84. data/lib/shopify_cli/theme/syncer.rb +17 -2
  85. data/lib/shopify_cli/theme/theme.rb +26 -4
  86. data/lib/shopify_cli/thread_pool/job.rb +27 -0
  87. data/lib/shopify_cli/thread_pool.rb +37 -0
  88. data/lib/shopify_cli/version.rb +1 -1
  89. data/lib/shopify_cli.rb +6 -1
  90. data/vendor/deps/cli-kit/lib/cli/kit/error_handler.rb +3 -1
  91. data/vendor/deps/ruby2_keywords/LICENSE +22 -0
  92. data/vendor/deps/ruby2_keywords/README.md +67 -0
  93. data/vendor/deps/ruby2_keywords/Rakefile +54 -0
  94. data/vendor/deps/ruby2_keywords/lib/ruby2_keywords.rb +57 -0
  95. data/vendor/deps/ruby2_keywords/ruby2_keywords.gemspec +18 -0
  96. data/vendor/deps/ruby2_keywords/test/test_keyword.rb +41 -0
  97. metadata +28 -4
  98. data/lib/graphql/all_orgs_with_extensions.graphql +0 -37
  99. data/lib/project_types/extension/tasks/run_extension_command.rb +0 -82
@@ -6,9 +6,26 @@ module Script
6
6
  class ScriptProjectRepository
7
7
  include SmartProperties
8
8
  property! :ctx, accepts: ShopifyCLI::Context
9
+ property :directory, accepts: String
10
+ property :initial_directory, accepts: String
9
11
 
10
12
  MUTABLE_ENV_VALUES = %i(uuid)
11
13
 
14
+ def create_project_directory
15
+ raise Infrastructure::Errors::ScriptProjectAlreadyExistsError, directory if ctx.dir_exist?(directory)
16
+ ctx.mkdir_p(directory)
17
+ change_directory(directory: directory)
18
+ end
19
+
20
+ def delete_project_directory
21
+ change_to_initial_directory
22
+ ctx.rm_r(directory)
23
+ end
24
+
25
+ def change_to_initial_directory
26
+ change_directory(directory: initial_directory)
27
+ end
28
+
12
29
  def create(script_name:, extension_point_type:, language:)
13
30
  validate_metadata!(extension_point_type, language)
14
31
 
@@ -95,6 +112,10 @@ module Script
95
112
 
96
113
  private
97
114
 
115
+ def change_directory(directory:)
116
+ ctx.chdir(directory)
117
+ end
118
+
98
119
  def capture_io(&block)
99
120
  CLI::UI::StdoutRouter::Capture.new(&block).run
100
121
  end
@@ -139,12 +160,15 @@ module Script
139
160
 
140
161
  def script_config_repository
141
162
  @script_config_repository ||= begin
163
+ script_config_yml_repo = ScriptConfigYmlRepository.new(ctx: ctx)
142
164
  supported_repos = [
143
- ScriptConfigYmlRepository.new(ctx: ctx),
165
+ script_config_yml_repo,
144
166
  ScriptJsonRepository.new(ctx: ctx),
145
167
  ]
146
168
  repo = supported_repos.find(&:active?)
147
- raise Infrastructure::Errors::NoScriptConfigYmlFileError if repo.nil?
169
+ if repo.nil?
170
+ raise Infrastructure::Errors::NoScriptConfigFileError, script_config_yml_repo.filename
171
+ end
148
172
  repo
149
173
  end
150
174
  end
@@ -158,7 +182,7 @@ module Script
158
182
  end
159
183
 
160
184
  def get!
161
- raise Infrastructure::Errors::NoScriptConfigFileError unless active?
185
+ raise Infrastructure::Errors::NoScriptConfigFileError, filename unless active?
162
186
 
163
187
  content = ctx.read(filename)
164
188
  hash = file_content_to_hash(content)
@@ -175,6 +199,10 @@ module Script
175
199
  from_h(hash)
176
200
  end
177
201
 
202
+ def filename
203
+ raise NotImplementedError
204
+ end
205
+
178
206
  private
179
207
 
180
208
  def update_hash(hash:, title:)
@@ -183,14 +211,7 @@ module Script
183
211
  end
184
212
 
185
213
  def from_h(hash)
186
- Domain::ScriptConfig.new(content: hash)
187
- rescue Domain::Errors::MissingScriptConfigFieldError => e
188
- raise missing_field_error, e.field
189
- end
190
-
191
- # to be implemented by subclasses
192
- def filename
193
- raise NotImplementedError
214
+ Domain::ScriptConfig.new(content: hash, filename: filename)
194
215
  end
195
216
 
196
217
  def file_content_to_hash(file_content)
@@ -200,26 +221,22 @@ module Script
200
221
  def hash_to_file_content(hash)
201
222
  raise NotImplementedError
202
223
  end
203
-
204
- def missing_field_error
205
- raise NotImplementedError
206
- end
207
224
  end
208
225
 
209
226
  class ScriptConfigYmlRepository < ScriptConfigRepository
210
- private
211
-
212
227
  def filename
213
228
  "script.config.yml"
214
229
  end
215
230
 
231
+ private
232
+
216
233
  def file_content_to_hash(file_content)
217
234
  begin
218
235
  hash = YAML.load(file_content)
219
236
  rescue Psych::SyntaxError
220
- raise Errors::InvalidScriptConfigYmlDefinitionError
237
+ raise parse_error
221
238
  end
222
- raise Errors::InvalidScriptConfigYmlDefinitionError unless hash.is_a?(Hash)
239
+ raise parse_error unless hash.is_a?(Hash)
223
240
  hash
224
241
  end
225
242
 
@@ -227,30 +244,34 @@ module Script
227
244
  YAML.dump(hash)
228
245
  end
229
246
 
230
- def missing_field_error
231
- Errors::MissingScriptConfigYmlFieldError
247
+ def parse_error
248
+ Errors::ScriptConfigParseError.new(filename: filename, serialization_format: "YAML")
232
249
  end
233
250
  end
234
251
 
235
252
  class ScriptJsonRepository < ScriptConfigRepository
236
- private
237
-
238
253
  def filename
239
254
  "script.json"
240
255
  end
241
256
 
257
+ private
258
+
242
259
  def file_content_to_hash(file_content)
243
- JSON.parse(file_content)
244
- rescue JSON::ParserError
245
- raise Errors::InvalidScriptJsonDefinitionError
260
+ begin
261
+ hash = JSON.parse(file_content)
262
+ rescue JSON::ParserError
263
+ raise parse_error
264
+ end
265
+ raise parse_error unless hash.is_a?(Hash)
266
+ hash
246
267
  end
247
268
 
248
269
  def hash_to_file_content(hash)
249
270
  JSON.pretty_generate(hash)
250
271
  end
251
272
 
252
- def missing_field_error
253
- Errors::MissingScriptJsonFieldError
273
+ def parse_error
274
+ Errors::ScriptConfigParseError.new(filename: filename, serialization_format: "JSON")
254
275
  end
255
276
  end
256
277
  end
@@ -46,20 +46,37 @@ module Script
46
46
 
47
47
  if user_errors.any? { |e| e["tag"] == "already_exists_error" }
48
48
  raise Errors::ScriptRepushError, uuid
49
+ elsif (e = user_errors.find { |err| err["tag"] == "configuration_definition_error" })
50
+ raise Errors::ScriptConfigurationDefinitionError.new(
51
+ message: e["message"],
52
+ filename: script_config.filename,
53
+ )
49
54
  elsif (e = user_errors.any? { |err| err["tag"] == "configuration_definition_syntax_error" })
50
- raise Errors::ScriptConfigSyntaxError
55
+ raise Errors::ScriptConfigSyntaxError, script_config.filename
51
56
  elsif (e = user_errors.find { |err| err["tag"] == "configuration_definition_missing_keys_error" })
52
- raise Errors::ScriptConfigMissingKeysError, e["message"]
57
+ raise Errors::ScriptConfigMissingKeysError.new(
58
+ missing_keys: e["message"],
59
+ filename: script_config.filename,
60
+ )
53
61
  elsif (e = user_errors.find { |err| err["tag"] == "configuration_definition_invalid_value_error" })
54
- raise Errors::ScriptConfigInvalidValueError, e["message"]
62
+ raise Errors::ScriptConfigInvalidValueError.new(
63
+ valid_input_modes: e["message"],
64
+ filename: script_config.filename,
65
+ )
55
66
  elsif (e = user_errors.find do |err|
56
67
  err["tag"] == "configuration_definition_schema_field_missing_keys_error"
57
68
  end)
58
- raise Errors::ScriptConfigFieldsMissingKeysError, e["message"]
69
+ raise Errors::ScriptConfigFieldsMissingKeysError.new(
70
+ missing_keys: e["message"],
71
+ filename: script_config.filename,
72
+ )
59
73
  elsif (e = user_errors.find do |err|
60
74
  err["tag"] == "configuration_definition_schema_field_invalid_value_error"
61
75
  end)
62
- raise Errors::ScriptConfigFieldsInvalidValueError, e["message"]
76
+ raise Errors::ScriptConfigFieldsInvalidValueError.new(
77
+ valid_types: e["message"],
78
+ filename: script_config.filename,
79
+ )
63
80
  elsif user_errors.find { |err| %w(not_use_msgpack_error schema_version_argument_error).include?(err["tag"]) }
64
81
  raise Domain::Errors::MetadataValidationError
65
82
  else
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Script
4
+ module Loaders
5
+ module Project
6
+ def self.load(directory:, api_key:, uuid:, api_secret:, context: ShopifyCLI::Context.new)
7
+ env_overrides = {
8
+ "SHOPIFY_API_KEY" => api_key,
9
+ "SHOPIFY_API_SECRET" => api_secret,
10
+ "UUID" => uuid,
11
+ }.compact
12
+ env_file_present = env_file_exists?(directory)
13
+ env = if env_file_present
14
+ ShopifyCLI::Resources::EnvFile.read(directory, overrides: env_overrides)
15
+ else
16
+ ShopifyCLI::Resources::EnvFile.from_hash(env_overrides)
17
+ end
18
+
19
+ project = ShopifyCLI::Project.at(directory)
20
+ project.env = env
21
+ project
22
+ rescue SmartProperties::InitializationError, SmartProperties::InvalidValueError => error
23
+ handle_error(error, context: context, env_file_present: env_file_present)
24
+ end
25
+
26
+ def self.handle_error(error, context:, env_file_present:)
27
+ if env_file_present
28
+ properties_hash = { api_key: "SHOPIFY_API_KEY", secret: "SHOPIFY_API_SECRET" }
29
+ missing_env_variables = error.properties.map { |p| properties_hash[p.name] }.compact.join(", ")
30
+ raise ShopifyCLI::Abort,
31
+ context.message("script.error.missing_env_file_variables", missing_env_variables, ShopifyCLI::TOOL_NAME)
32
+ else
33
+ properties_hash = { api_key: "--api-key", secret: "--api-secret" }
34
+ missing_options = error.properties.map { |p| properties_hash[p.name] }.compact.join(", ")
35
+ raise ShopifyCLI::Abort, context.message("script.error.missing_push_options", missing_options)
36
+ end
37
+ end
38
+
39
+ def self.env_file_exists?(directory)
40
+ File.exist?(ShopifyCLI::Resources::EnvFile.path(directory))
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Script
4
+ module Loaders
5
+ module SpecificationHandler
6
+ def self.load(project:, context:)
7
+ identifier = project.specification_identifier
8
+ Models::LazySpecificationHandler.new(identifier) do
9
+ specifications = Models::Specifications.new(
10
+ fetch_specifications: Tasks::FetchSpecifications.new(api_key: project.app.api_key, context: context)
11
+ )
12
+
13
+ unless specifications.valid?(identifier)
14
+ context.abort(context.message("errors.unknown_type", project.specification_identifier))
15
+ end
16
+
17
+ specifications[identifier]
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -47,38 +47,39 @@ module Script
47
47
  invalid_language_cause: "Invalid language %s.",
48
48
  invalid_language_help: "Allowed values: %s.",
49
49
 
50
- missing_script_config_yml_field_cause: "The script.config.yml file is missing the required %s field.",
51
- missing_script_config_yml_field_help: "Add the field and try again.",
50
+ 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.",
52
52
 
53
- missing_script_json_field_cause: "The script.json file is missing the required %s field.",
54
- missing_script_json_field_help: "Add the field and try again.",
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.",
55
55
 
56
- invalid_script_json_definition_cause: "The script.json file contains invalid JSON.",
57
- invalid_script_json_definition_help: "Fix the errors and try again.",
56
+ no_script_config_file_cause: "The %{filename} file is missing.",
57
+ no_script_config_file_help: "Create this file and try again.",
58
58
 
59
- invalid_script_config_yml_definition_cause: "The script.config.yml file contains invalid YAML.",
60
- invalid_script_config_yml_definition_help: "Fix the errors and try again.",
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.",
61
61
 
62
- no_script_config_yml_file_cause: "The script.config.yml file is missing.",
63
- no_script_config_yml_file_help: "Create this file and try again.",
62
+ configuration_definition_error_cause: "In the %{filename} file, there was a problem with the "\
63
+ "configuration. %{message}",
64
+ configuration_definition_error_help: "Fix the error and try again.",
64
65
 
65
- configuration_syntax_error_cause: "The script.json is not formatted properly.",
66
+ configuration_syntax_error_cause: "The %{filename} is not formatted properly.",
66
67
  configuration_syntax_error_help: "Fix the errors and try again.",
67
68
 
68
- configuration_missing_keys_error_cause: "The script.json file is missing required keys: "\
69
+ configuration_missing_keys_error_cause: "The %{filename} file is missing required keys: "\
69
70
  "%{missing_keys}.",
70
71
  configuration_missing_keys_error_help: "Add the keys and try again.",
71
72
 
72
- configuration_invalid_value_error_cause: "The script.json configuration only accepts "\
73
+ configuration_invalid_value_error_cause: "The %{filename} configuration only accepts "\
73
74
  "one of the following types(s): %{valid_input_modes}.",
74
75
  configuration_invalid_value_error_help: "Change the type and try again.",
75
76
 
76
- configuration_schema_field_missing_keys_error_cause: "A configuration entry in the script.json file "\
77
+ configuration_schema_field_missing_keys_error_cause: "A configuration entry in the %{filename} file "\
77
78
  "is missing required keys: %{missing_keys}.",
78
79
  configuration_definition_schema_field_missing_keys_error_help: "Add the keys and try again.",
79
80
 
80
81
  configuration_schema_field_invalid_value_error_cause: "The configuration entries in the "\
81
- "script.json file only accept one of the following "\
82
+ "%{filename} file only accept one of the following "\
82
83
  "type(s): %{valid_types}.",
83
84
  configuration_schema_field_invalid_value_error_help: "Change the types and try again.",
84
85
 
@@ -144,6 +145,12 @@ module Script
144
145
 
145
146
  language_library_for_api_not_found_cause: "Script can’t be pushed because the %{language} library for API %{api} is missing.",
146
147
  language_library_for_api_not_found_help: "Make sure extension_point.yml contains the correct API library.",
148
+ no_scripts_found_in_app: "The selected apps have no scripts. Please, create them first on the partners' dashboard.",
149
+ missing_env_file_variables: "The following variables are missing in the .env file: %s."\
150
+ " It might happen when the script hasn't been previously connected to an app."\
151
+ " To connect the script to an app, run {{command:%s script connect}}",
152
+ missing_push_options: "The following options are required: %s."\
153
+ " You can obtain them from the .env file generated after connecting the script to an app.",
147
154
  },
148
155
 
149
156
  create: {
@@ -153,6 +160,7 @@ module Script
153
160
  Options:
154
161
  {{command:--name=NAME}} Script project name. Use any string.
155
162
  {{command:--api=TYPE}} Script API name. Allowed values: %2$s.
163
+ {{command:--language=LANGUAGE}} Programming language. Allowed values: %3$s.
156
164
  HELP
157
165
 
158
166
  error: {
@@ -170,16 +178,31 @@ module Script
170
178
  Usage: {{command:%s script push}}
171
179
  Options:
172
180
  {{command:[--force]}} Replaces the existing script on the app with this version.
181
+ {{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.
182
+ {{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.
183
+ {{command:[--uuid=UUID]}} The uuid of the script. Overrides the value in the .env file, if present.
173
184
  HELP
174
185
 
175
186
  error: {
187
+ operation_failed_no_uuid: "UUID is required to push in a CI environment.",
176
188
  operation_failed_with_api_key: "Couldn't push script to app (API key: %{api_key}).",
177
189
  operation_failed_no_api_key: "Couldn't push script to app.",
178
190
  },
179
191
 
180
192
  script_pushed: "{{v}} Script pushed to app (API key: %{api_key}).",
181
193
  },
182
-
194
+ connect: {
195
+ connected: "Connected! Your project is now connected to {{green:%s}}",
196
+ help: <<~HELP,
197
+ {{command:%s script connect}}: Connects an existing script to an app.
198
+ Usage: {{command:%s script connect}}
199
+ HELP
200
+ error: {
201
+ operation_failed: "Couldn't connect script to app.",
202
+ missing_env_file_variables: "The following variables are missing in the .env file: %s."\
203
+ " 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}}",
204
+ },
205
+ },
183
206
  javy: {
184
207
  help: <<~HELP,
185
208
  Compile the JavaScript code into WebAssembly.
@@ -103,51 +103,65 @@ module Script
103
103
  cause_of_error: ShopifyCLI::Context.message("script.error.metadata_not_found_cause"),
104
104
  help_suggestion: ShopifyCLI::Context.message("script.error.metadata_not_found_help"),
105
105
  }
106
+ when Layers::Domain::Errors::MissingScriptConfigFieldError
107
+ {
108
+ cause_of_error: ShopifyCLI::Context.message(
109
+ "script.error.missing_script_config_field_cause",
110
+ field: e.field,
111
+ filename: e.filename,
112
+ ),
113
+ help_suggestion: ShopifyCLI::Context.message("script.error.missing_script_config_field_help"),
114
+ }
106
115
  when Layers::Infrastructure::Errors::BuildError
107
116
  {
108
117
  cause_of_error: ShopifyCLI::Context.message("script.error.build_error_cause"),
109
118
  help_suggestion: ShopifyCLI::Context.message("script.error.build_error_help"),
110
119
  }
111
- when Layers::Infrastructure::Errors::InvalidScriptConfigYmlDefinitionError
112
- {
113
- cause_of_error: ShopifyCLI::Context.message("script.error.invalid_script_config_yml_definition_cause"),
114
- help_suggestion: ShopifyCLI::Context.message("script.error.invalid_script_config_yml_definition_help"),
115
- }
116
- when Layers::Infrastructure::Errors::InvalidScriptJsonDefinitionError
117
- {
118
- cause_of_error: ShopifyCLI::Context.message("script.error.invalid_script_json_definition_cause"),
119
- help_suggestion: ShopifyCLI::Context.message("script.error.invalid_script_json_definition_help"),
120
- }
121
- when Layers::Infrastructure::Errors::MissingScriptConfigYmlFieldError
122
- {
123
- cause_of_error: ShopifyCLI::Context.message("script.error.missing_script_config_yml_field_cause", e.field),
124
- help_suggestion: ShopifyCLI::Context.message("script.error.missing_script_config_yml_field_help"),
125
- }
126
- when Layers::Infrastructure::Errors::MissingScriptConfigYmlFieldError
120
+ when Layers::Infrastructure::Errors::ScriptConfigParseError
127
121
  {
128
- cause_of_error: ShopifyCLI::Context.message("script.error.missing_script_config_yml_field_cause", e.field),
129
- help_suggestion: ShopifyCLI::Context.message("script.error.missing_script_config_yml_field_help"),
122
+ cause_of_error: ShopifyCLI::Context.message(
123
+ "script.error.script_config_parse_error_cause",
124
+ filename: e.filename,
125
+ serialization_format: e.serialization_format,
126
+ ),
127
+ help_suggestion: ShopifyCLI::Context.message("script.error.script_config_parse_error_help"),
130
128
  }
131
- when Layers::Infrastructure::Errors::MissingScriptJsonFieldError
129
+ when Layers::Infrastructure::Errors::NoScriptConfigFileError
132
130
  {
133
- cause_of_error: ShopifyCLI::Context.message("script.error.missing_script_json_field_cause", e.field),
134
- help_suggestion: ShopifyCLI::Context.message("script.error.missing_script_json_field_help"),
131
+ cause_of_error: ShopifyCLI::Context.message(
132
+ "script.error.no_script_config_file_cause",
133
+ filename: e.filename,
134
+ ),
135
+ help_suggestion: ShopifyCLI::Context.message("script.error.no_script_config_file_help"),
135
136
  }
136
- when Layers::Infrastructure::Errors::NoScriptConfigYmlFileError
137
+ when Layers::Infrastructure::Errors::ScriptConfigurationDefinitionError
137
138
  {
138
- cause_of_error: ShopifyCLI::Context.message("script.error.no_script_config_yml_file_cause"),
139
- help_suggestion: ShopifyCLI::Context.message("script.error.no_script_config_yml_file_help"),
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"),
140
145
  }
141
146
  when Layers::Infrastructure::Errors::ScriptConfigSyntaxError
142
147
  {
143
- cause_of_error: ShopifyCLI::Context.message("script.error.configuration_syntax_error_cause"),
148
+ cause_of_error: ShopifyCLI::Context.message(
149
+ "script.error.configuration_syntax_error_cause",
150
+ filename: e.filename,
151
+ ),
144
152
  help_suggestion: ShopifyCLI::Context.message("script.error.configuration_syntax_error_help"),
145
153
  }
154
+ when Layers::Infrastructure::Errors::ScriptEnvAppNotConnectedError
155
+ {
156
+ cause_of_error: ShopifyCLI::Context.message("script.error.app_not_connected_cause"),
157
+ help_suggestion: ShopifyCLI::Context.message("script.error.app_not_connected_help"),
158
+ }
146
159
  when Layers::Infrastructure::Errors::ScriptConfigMissingKeysError
147
160
  {
148
161
  cause_of_error: ShopifyCLI::Context.message(
149
162
  "script.error.configuration_missing_keys_error_cause",
150
- missing_keys: e.missing_keys
163
+ missing_keys: e.missing_keys,
164
+ filename: e.filename,
151
165
  ),
152
166
  help_suggestion: ShopifyCLI::Context.message("script.error.configuration_missing_keys_error_help"),
153
167
  }
@@ -155,7 +169,8 @@ module Script
155
169
  {
156
170
  cause_of_error: ShopifyCLI::Context.message(
157
171
  "script.error.configuration_invalid_value_error_cause",
158
- valid_input_modes: e.valid_input_modes
172
+ valid_input_modes: e.valid_input_modes,
173
+ filename: e.filename,
159
174
  ),
160
175
  help_suggestion: ShopifyCLI::Context.message("script.error.configuration_invalid_value_error_help"),
161
176
  }
@@ -163,7 +178,8 @@ module Script
163
178
  {
164
179
  cause_of_error: ShopifyCLI::Context.message(
165
180
  "script.error.configuration_schema_field_missing_keys_error_cause",
166
- missing_keys: e.missing_keys
181
+ missing_keys: e.missing_keys,
182
+ filename: e.filename,
167
183
  ),
168
184
  help_suggestion: ShopifyCLI::Context.message(
169
185
  "script.error.configuration_definition_schema_field_missing_keys_error_help"
@@ -173,7 +189,8 @@ module Script
173
189
  {
174
190
  cause_of_error: ShopifyCLI::Context.message(
175
191
  "script.error.configuration_schema_field_invalid_value_error_cause",
176
- valid_types: e.valid_types
192
+ valid_types: e.valid_types,
193
+ filename: e.filename,
177
194
  ),
178
195
  help_suggestion: ShopifyCLI::Context.message(
179
196
  "script.error.configuration_schema_field_invalid_value_error_help"
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require "shopify_cli/theme/theme"
3
3
  require "shopify_cli/theme/ignore_filter"
4
+ require "shopify_cli/theme/include_filter"
4
5
  require "shopify_cli/theme/syncer"
5
6
 
6
7
  module Theme
@@ -9,7 +10,10 @@ module Theme
9
10
  options do |parser, flags|
10
11
  parser.on("-n", "--nodelete") { flags[:nodelete] = true }
11
12
  parser.on("-i", "--themeid=ID") { |theme_id| flags[:theme_id] = theme_id }
13
+ parser.on("-t", "--theme=NAME_OR_ID") { |theme| flags[:theme] = theme }
12
14
  parser.on("-l", "--live") { flags[:live] = true }
15
+ parser.on("-d", "--development") { flags[:development] = true }
16
+ parser.on("-o", "--only=PATTERN") { |pattern| flags[:includes] = pattern }
13
17
  parser.on("-x", "--ignore=PATTERN") do |pattern|
14
18
  flags[:ignores] ||= []
15
19
  flags[:ignores] << pattern
@@ -19,26 +23,16 @@ module Theme
19
23
  def call(args, _name)
20
24
  root = args.first || "."
21
25
  delete = !options.flags[:nodelete]
26
+ theme = find_theme(root, **options.flags)
27
+ return if theme.nil?
22
28
 
23
- theme = if (theme_id = options.flags[:theme_id])
24
- ShopifyCLI::Theme::Theme.new(@ctx, root: root, id: theme_id)
25
- elsif options.flags[:live]
26
- ShopifyCLI::Theme::Theme.live(@ctx, root: root)
27
- else
28
- form = Forms::Select.ask(
29
- @ctx,
30
- [],
31
- title: @ctx.message("theme.pull.select"),
32
- root: root,
33
- )
34
- return unless form
35
- form.theme
36
- end
37
-
29
+ include_filter = ShopifyCLI::Theme::IncludeFilter.new(options.flags[:includes])
38
30
  ignore_filter = ShopifyCLI::Theme::IgnoreFilter.from_path(root)
39
31
  ignore_filter.add_patterns(options.flags[:ignores]) if options.flags[:ignores]
40
32
 
41
- syncer = ShopifyCLI::Theme::Syncer.new(@ctx, theme: theme, ignore_filter: ignore_filter)
33
+ syncer = ShopifyCLI::Theme::Syncer.new(@ctx, theme: theme,
34
+ include_filter: include_filter,
35
+ ignore_filter: ignore_filter)
42
36
  begin
43
37
  syncer.start_threads
44
38
  CLI::UI::Frame.open(@ctx.message("theme.pull.pulling", theme.name, theme.id, theme.shop)) do
@@ -46,7 +40,7 @@ module Theme
46
40
  end
47
41
  @ctx.done(@ctx.message("theme.pull.done"))
48
42
  rescue ShopifyCLI::API::APIRequestNotFoundError
49
- @ctx.abort(@ctx.message("theme.pull.theme_not_found", theme.id))
43
+ @ctx.abort(@ctx.message("theme.pull.theme_not_found", "##{theme.id}"))
50
44
  ensure
51
45
  syncer.shutdown
52
46
  end
@@ -55,6 +49,40 @@ module Theme
55
49
  def self.help
56
50
  ShopifyCLI::Context.message("theme.pull.help", ShopifyCLI::TOOL_NAME, ShopifyCLI::TOOL_NAME)
57
51
  end
52
+
53
+ private
54
+
55
+ def find_theme(root, theme_id: nil, theme: nil, live: nil, development: nil, **_args)
56
+ if theme_id
57
+ @ctx.warn(@ctx.message("theme.pull.deprecated_themeid"))
58
+ return ShopifyCLI::Theme::Theme.new(@ctx, root: root, id: theme_id)
59
+ end
60
+
61
+ if theme
62
+ selected_theme = ShopifyCLI::Theme::Theme.find_by_identifier(@ctx, root: root, identifier: theme)
63
+ return selected_theme || @ctx.abort(@ctx.message("theme.pull.theme_not_found", theme))
64
+ end
65
+
66
+ if live
67
+ return ShopifyCLI::Theme::Theme.live(@ctx, root: root)
68
+ end
69
+
70
+ if development
71
+ return ShopifyCLI::Theme::Theme.development(@ctx, root: root)
72
+ end
73
+
74
+ select_theme(root)
75
+ end
76
+
77
+ def select_theme(root)
78
+ form = Forms::Select.ask(
79
+ @ctx,
80
+ [],
81
+ title: @ctx.message("theme.pull.select"),
82
+ root: root,
83
+ )
84
+ form&.theme
85
+ end
58
86
  end
59
87
  end
60
88
  end