shopify-cli 2.8.0 → 2.10.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE.md +18 -0
  3. data/CHANGELOG.md +34 -0
  4. data/Gemfile.lock +1 -1
  5. data/RELEASING.md +4 -3
  6. data/ext/javy/javy.rb +1 -1
  7. data/lib/project_types/extension/commands/push.rb +2 -2
  8. data/lib/project_types/extension/messages/messages.rb +1 -1
  9. data/lib/project_types/extension/models/development_server.rb +2 -4
  10. data/lib/project_types/rails/gem.rb +1 -2
  11. data/lib/project_types/script/cli.rb +10 -0
  12. data/lib/project_types/script/commands/connect.rb +1 -1
  13. data/lib/project_types/script/commands/create.rb +8 -2
  14. data/lib/project_types/script/commands/push.rb +35 -12
  15. data/lib/project_types/script/config/extension_points.yml +12 -0
  16. data/lib/project_types/script/graphql/app_script_set.graphql +2 -0
  17. data/lib/project_types/script/graphql/module_upload_url_generate.graphql +5 -1
  18. data/lib/project_types/script/layers/application/build_script.rb +6 -3
  19. data/lib/project_types/script/layers/application/connect_app.rb +11 -5
  20. data/lib/project_types/script/layers/application/extension_points.rb +50 -26
  21. data/lib/project_types/script/layers/application/project_dependencies.rb +1 -1
  22. data/lib/project_types/script/layers/application/push_script.rb +41 -30
  23. data/lib/project_types/script/layers/domain/errors.rb +10 -3
  24. data/lib/project_types/script/layers/domain/extension_point.rb +16 -2
  25. data/lib/project_types/script/layers/domain/push_package.rb +0 -3
  26. data/lib/project_types/script/layers/domain/script_config.rb +6 -4
  27. data/lib/project_types/script/layers/domain/script_project.rb +1 -0
  28. data/lib/project_types/script/layers/infrastructure/errors.rb +47 -23
  29. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +2 -12
  30. data/lib/project_types/script/layers/infrastructure/languages/project_creator.rb +1 -0
  31. data/lib/project_types/script/layers/infrastructure/languages/task_runner.rb +1 -0
  32. data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +2 -12
  33. data/lib/project_types/script/layers/infrastructure/languages/wasm_project_creator.rb +15 -0
  34. data/lib/project_types/script/layers/infrastructure/languages/wasm_task_runner.rb +36 -0
  35. data/lib/project_types/script/layers/infrastructure/metadata_repository.rb +18 -0
  36. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +7 -8
  37. data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +45 -54
  38. data/lib/project_types/script/layers/infrastructure/script_service.rb +35 -12
  39. data/lib/project_types/script/layers/infrastructure/script_uploader.rb +22 -9
  40. data/lib/project_types/script/loaders/project.rb +44 -0
  41. data/lib/project_types/script/loaders/specification_handler.rb +22 -0
  42. data/lib/project_types/script/messages/messages.rb +38 -19
  43. data/lib/project_types/script/ui/error_handler.rb +52 -30
  44. data/lib/project_types/theme/commands/pull.rb +45 -17
  45. data/lib/project_types/theme/commands/push.rb +62 -27
  46. data/lib/project_types/theme/commands/serve.rb +5 -0
  47. data/lib/project_types/theme/messages/messages.rb +33 -18
  48. data/lib/shopify_cli/constants.rb +7 -2
  49. data/lib/shopify_cli/context.rb +66 -12
  50. data/lib/shopify_cli/core/executor.rb +4 -4
  51. data/lib/shopify_cli/environment.rb +50 -20
  52. data/lib/shopify_cli/identity_auth.rb +4 -3
  53. data/lib/shopify_cli/messages/messages.rb +2 -0
  54. data/lib/shopify_cli/method_object.rb +21 -9
  55. data/lib/shopify_cli/resources/env_file.rb +5 -1
  56. data/lib/shopify_cli/result.rb +61 -59
  57. data/lib/shopify_cli/task.rb +5 -3
  58. data/lib/shopify_cli/theme/dev_server/hot-reload.js +19 -1
  59. data/lib/shopify_cli/theme/dev_server/hot_reload.rb +18 -2
  60. data/lib/shopify_cli/theme/dev_server/proxy.rb +1 -0
  61. data/lib/shopify_cli/theme/dev_server/reload_mode.rb +34 -0
  62. data/lib/shopify_cli/theme/dev_server.rb +6 -21
  63. data/lib/shopify_cli/theme/file.rb +2 -2
  64. data/lib/shopify_cli/theme/filter/path_matcher.rb +38 -0
  65. data/lib/shopify_cli/theme/ignore_filter.rb +14 -18
  66. data/lib/shopify_cli/theme/include_filter.rb +43 -0
  67. data/lib/shopify_cli/theme/syncer.rb +17 -2
  68. data/lib/shopify_cli/theme/theme.rb +26 -4
  69. data/lib/shopify_cli/version.rb +1 -1
  70. data/lib/shopify_cli.rb +6 -1
  71. data/vendor/deps/cli-kit/lib/cli/kit/system.rb +10 -5
  72. data/vendor/deps/cli-ui/lib/cli/ui/os.rb +6 -4
  73. data/vendor/deps/ruby2_keywords/LICENSE +22 -0
  74. data/vendor/deps/ruby2_keywords/README.md +67 -0
  75. data/vendor/deps/ruby2_keywords/Rakefile +54 -0
  76. data/vendor/deps/ruby2_keywords/lib/ruby2_keywords.rb +57 -0
  77. data/vendor/deps/ruby2_keywords/ruby2_keywords.gemspec +18 -0
  78. data/vendor/deps/ruby2_keywords/test/test_keyword.rb +41 -0
  79. metadata +16 -2
@@ -19,7 +19,8 @@ module Script
19
19
  metadata:,
20
20
  script_config:,
21
21
  module_upload_url:,
22
- library:
22
+ library:,
23
+ input_query: nil
23
24
  )
24
25
  query_name = "app_script_set"
25
26
  variables = {
@@ -34,11 +35,14 @@ module Script
34
35
  configurationUi: script_config.configuration_ui,
35
36
  configurationDefinition: script_config.configuration&.to_json,
36
37
  moduleUploadUrl: module_upload_url,
37
- library: {
38
- language: library[:language],
39
- version: library[:version],
40
- },
38
+ inputQuery: input_query,
41
39
  }
40
+
41
+ variables[:library] = {
42
+ language: library[:language],
43
+ version: library[:version],
44
+ } if library
45
+
42
46
  resp_hash = make_request(query_name: query_name, variables: variables)
43
47
  user_errors = resp_hash["data"]["appScriptSet"]["userErrors"]
44
48
 
@@ -46,20 +50,37 @@ module Script
46
50
 
47
51
  if user_errors.any? { |e| e["tag"] == "already_exists_error" }
48
52
  raise Errors::ScriptRepushError, uuid
53
+ elsif (e = user_errors.find { |err| err["tag"] == "configuration_definition_error" })
54
+ raise Errors::ScriptConfigurationDefinitionError.new(
55
+ message: e["message"],
56
+ filename: script_config.filename,
57
+ )
49
58
  elsif (e = user_errors.any? { |err| err["tag"] == "configuration_definition_syntax_error" })
50
- raise Errors::ScriptConfigSyntaxError
59
+ raise Errors::ScriptConfigSyntaxError, script_config.filename
51
60
  elsif (e = user_errors.find { |err| err["tag"] == "configuration_definition_missing_keys_error" })
52
- raise Errors::ScriptConfigMissingKeysError, e["message"]
61
+ raise Errors::ScriptConfigMissingKeysError.new(
62
+ missing_keys: e["message"],
63
+ filename: script_config.filename,
64
+ )
53
65
  elsif (e = user_errors.find { |err| err["tag"] == "configuration_definition_invalid_value_error" })
54
- raise Errors::ScriptConfigInvalidValueError, e["message"]
66
+ raise Errors::ScriptConfigInvalidValueError.new(
67
+ valid_input_modes: e["message"],
68
+ filename: script_config.filename,
69
+ )
55
70
  elsif (e = user_errors.find do |err|
56
71
  err["tag"] == "configuration_definition_schema_field_missing_keys_error"
57
72
  end)
58
- raise Errors::ScriptConfigFieldsMissingKeysError, e["message"]
73
+ raise Errors::ScriptConfigFieldsMissingKeysError.new(
74
+ missing_keys: e["message"],
75
+ filename: script_config.filename,
76
+ )
59
77
  elsif (e = user_errors.find do |err|
60
78
  err["tag"] == "configuration_definition_schema_field_invalid_value_error"
61
79
  end)
62
- raise Errors::ScriptConfigFieldsInvalidValueError, e["message"]
80
+ raise Errors::ScriptConfigFieldsInvalidValueError.new(
81
+ valid_types: e["message"],
82
+ filename: script_config.filename,
83
+ )
63
84
  elsif user_errors.find { |err| %w(not_use_msgpack_error schema_version_argument_error).include?(err["tag"]) }
64
85
  raise Domain::Errors::MetadataValidationError
65
86
  else
@@ -74,14 +95,16 @@ module Script
74
95
  response["data"]["appScripts"]
75
96
  end
76
97
 
77
- def generate_module_upload_url
98
+ def generate_module_upload_details
78
99
  query_name = "module_upload_url_generate"
79
100
  variables = {}
80
101
  response = make_request(query_name: query_name, variables: variables)
81
102
  user_errors = response["data"]["moduleUploadUrlGenerate"]["userErrors"]
82
103
 
83
104
  raise Errors::GraphqlError, user_errors if user_errors.any?
84
- response["data"]["moduleUploadUrlGenerate"]["url"]
105
+
106
+ data = response["data"]["moduleUploadUrlGenerate"]["details"]
107
+ { url: data["url"], headers: data["headers"], max_size: data["humanizedMaxSize"] }
85
108
  end
86
109
 
87
110
  private
@@ -7,19 +7,32 @@ module Script
7
7
  end
8
8
 
9
9
  def upload(script_content)
10
- @script_service.generate_module_upload_url.tap do |url|
11
- url = URI(url)
10
+ upload_details = @script_service.generate_module_upload_details
11
+ url = URI(upload_details[:url])
12
12
 
13
- https = Net::HTTP.new(url.host, url.port)
14
- https.use_ssl = true
13
+ https = Net::HTTP.new(url.host, url.port)
14
+ https.use_ssl = true
15
15
 
16
- request = Net::HTTP::Put.new(url)
17
- request["Content-Type"] = "application/wasm"
18
- request.body = script_content
16
+ request = Net::HTTP::Put.new(url)
17
+ request["Content-Type"] = "application/wasm"
19
18
 
20
- response = https.request(request)
21
- raise Errors::ScriptUploadError unless response.code == "200"
19
+ upload_details[:headers].each do |header, value|
20
+ request[header] = value
22
21
  end
22
+
23
+ request.body = script_content
24
+
25
+ response = https.request(request)
26
+ raise Errors::ScriptTooLargeError, upload_details[:max_size] if script_too_large?(response)
27
+ raise Errors::ScriptUploadError unless response.code == "200"
28
+
29
+ upload_details[:url]
30
+ end
31
+
32
+ private
33
+
34
+ def script_too_large?(response)
35
+ response.code == "400" && response.body.include?("EntityTooLarge")
23
36
  end
24
37
  end
25
38
  end
@@ -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
 
@@ -128,8 +129,8 @@ module Script
128
129
  "\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=",
129
130
 
130
131
  web_assembly_binary_not_found: "WebAssembly binary not found.",
131
- web_assembly_binary_not_found_suggestion: "No WebAssembly binary found." \
132
- "Check that your build npm script outputs the generated binary to the root of the directory." \
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. " \
133
134
  "Generated binary should match the script name: <script_name>.wasm",
134
135
 
135
136
  project_config_not_found: "Internal error - Script can't be created because the project's config file is missing from the repository.",
@@ -139,12 +140,20 @@ module Script
139
140
  script_upload_cause: "Fail to upload script.",
140
141
  script_upload_help: "Try again.",
141
142
 
143
+ script_too_large_cause: "The size of your Wasm binary file is too large.",
144
+ script_too_large_help: "It must be less than %{max_size}.",
145
+
142
146
  api_library_not_found_cause: "Script can't be created because API library %{library_name} is missing from the dependencies",
143
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.",
144
148
 
145
149
  language_library_for_api_not_found_cause: "Script can’t be pushed because the %{language} library for API %{api} is missing.",
146
150
  language_library_for_api_not_found_help: "Make sure extension_point.yml contains the correct API library.",
147
151
  no_scripts_found_in_app: "The selected apps have no scripts. Please, create them first on the partners' dashboard.",
152
+ 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
+ " 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.",
148
157
  },
149
158
 
150
159
  create: {
@@ -154,6 +163,7 @@ module Script
154
163
  Options:
155
164
  {{command:--name=NAME}} Script project name. Use any string.
156
165
  {{command:--api=TYPE}} Script API name. Allowed values: %2$s.
166
+ {{command:--language=LANGUAGE}} Programming language. Allowed values: %3$s.
157
167
  HELP
158
168
 
159
169
  error: {
@@ -163,6 +173,9 @@ module Script
163
173
  change_directory_notice: "{{*}} Change directories to {{green:%s}} to run script commands",
164
174
  creating: "Creating script",
165
175
  created: "Created script",
176
+ preparing_project: "Preparing script project structure",
177
+ creating_wasm: "Creating configuration files",
178
+ created_wasm: "Configuration files created",
166
179
  },
167
180
 
168
181
  push: {
@@ -171,9 +184,13 @@ module Script
171
184
  Usage: {{command:%s script push}}
172
185
  Options:
173
186
  {{command:[--force]}} Replaces the existing script on the app with this version.
187
+ {{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
+ {{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
+ {{command:[--uuid=UUID]}} The uuid of the script. Overrides the value in the .env file, if present.
174
190
  HELP
175
191
 
176
192
  error: {
193
+ operation_failed_no_uuid: "UUID is required to push in a CI environment.",
177
194
  operation_failed_with_api_key: "Couldn't push script to app (API key: %{api_key}).",
178
195
  operation_failed_no_api_key: "Couldn't push script to app.",
179
196
  },
@@ -182,13 +199,14 @@ module Script
182
199
  },
183
200
  connect: {
184
201
  connected: "Connected! Your project is now connected to {{green:%s}}",
185
- missing_script: "No script has been selected.",
186
202
  help: <<~HELP,
187
203
  {{command:%s script connect}}: Connects an existing script to an app.
188
204
  Usage: {{command:%s script connect}}
189
205
  HELP
190
206
  error: {
191
207
  operation_failed: "Couldn't connect script to app.",
208
+ 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}}",
192
210
  },
193
211
  },
194
212
  javy: {
@@ -206,7 +224,7 @@ module Script
206
224
 
207
225
  project_deps: {
208
226
  none_required: "{{v}} None required",
209
- checking_with_npm: "Checking dependencies with npm",
227
+ checking: "Checking dependencies",
210
228
  installing: "Dependencies installing",
211
229
  installed: "Missing dependencies installed",
212
230
  },
@@ -224,6 +242,7 @@ module Script
224
242
  building_script: "Building script",
225
243
  built: "Built",
226
244
  pushing: "Pushing",
245
+ pushing_script: "Pushing script",
227
246
  pushed: "Pushed",
228
247
  ensure_env: {
229
248
  organization: "Partner organization {{green:%s (%s)}}.",
@@ -100,54 +100,68 @@ module Script
100
100
  }
101
101
  when Layers::Domain::Errors::MetadataNotFoundError
102
102
  {
103
- cause_of_error: ShopifyCLI::Context.message("script.error.metadata_not_found_cause"),
103
+ cause_of_error: ShopifyCLI::Context.message("script.error.metadata_not_found_cause", e.filename),
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
120
+ when Layers::Infrastructure::Errors::ScriptConfigParseError
117
121
  {
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
127
- {
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"
@@ -234,6 +251,11 @@ module Script
234
251
  cause_of_error: ShopifyCLI::Context.message("script.error.script_upload_cause"),
235
252
  help_suggestion: ShopifyCLI::Context.message("script.error.script_upload_help"),
236
253
  }
254
+ when Layers::Infrastructure::Errors::ScriptTooLargeError
255
+ {
256
+ cause_of_error: ShopifyCLI::Context.message("script.error.script_too_large_cause"),
257
+ help_suggestion: ShopifyCLI::Context.message("script.error.script_too_large_help", max_size: e.max_size),
258
+ }
237
259
  when Layers::Infrastructure::Errors::APILibraryNotFoundError
238
260
  {
239
261
  cause_of_error: ShopifyCLI::Context
@@ -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