shopify-cli 2.11.1 → 2.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/shopify.yml +2 -1
  3. data/.rubocop.yml +1 -1
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +32 -0
  6. data/Gemfile.lock +14 -14
  7. data/bin/shopify +17 -4
  8. data/dev.yml +1 -1
  9. data/docs/users/installation.md +1 -44
  10. data/ext/javy/hashes/javy-arm-macos-v0.2.0.gz.sha256 +1 -0
  11. data/ext/javy/hashes/javy-x86_64-linux-v0.2.0.gz.sha256 +1 -0
  12. data/ext/javy/hashes/javy-x86_64-macos-v0.2.0.gz.sha256 +1 -0
  13. data/ext/javy/hashes/javy-x86_64-windows-v0.2.0.gz.sha256 +1 -0
  14. data/ext/javy/version +1 -1
  15. data/lib/project_types/extension/commands/build.rb +0 -3
  16. data/lib/project_types/extension/commands/check.rb +0 -1
  17. data/lib/project_types/extension/commands/create.rb +0 -1
  18. data/lib/project_types/extension/commands/push.rb +0 -1
  19. data/lib/project_types/extension/commands/serve.rb +0 -1
  20. data/lib/project_types/extension/features/argo_setup_steps.rb +4 -6
  21. data/lib/project_types/extension/models/specification_handlers/checkout_ui_extension.rb +114 -0
  22. data/lib/project_types/extension/models/specification_handlers/theme_app_extension.rb +7 -1
  23. data/lib/project_types/extension/tasks/configure_features.rb +15 -2
  24. data/lib/project_types/extension/tasks/convert_server_config.rb +2 -1
  25. data/lib/project_types/script/cli.rb +2 -0
  26. data/lib/project_types/script/commands/create.rb +5 -5
  27. data/lib/project_types/script/commands/push.rb +4 -6
  28. data/lib/project_types/script/config/extension_points.yml +0 -4
  29. data/lib/project_types/script/errors.rb +1 -1
  30. data/lib/project_types/script/forms/create.rb +7 -20
  31. data/lib/project_types/script/layers/application/build_script.rb +22 -24
  32. data/lib/project_types/script/layers/application/connect_app.rb +3 -2
  33. data/lib/project_types/script/layers/application/create_script.rb +9 -10
  34. data/lib/project_types/script/layers/application/project_dependencies.rb +12 -14
  35. data/lib/project_types/script/layers/application/push_script.rb +2 -0
  36. data/lib/project_types/script/layers/domain/errors.rb +3 -3
  37. data/lib/project_types/script/layers/domain/push_package.rb +6 -0
  38. data/lib/project_types/script/layers/domain/script_config.rb +2 -4
  39. data/lib/project_types/script/layers/domain/script_project.rb +3 -2
  40. data/lib/project_types/script/layers/infrastructure/errors.rb +11 -0
  41. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_project_creator.rb +20 -9
  42. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +29 -18
  43. data/lib/project_types/script/layers/infrastructure/languages/project_creator.rb +0 -15
  44. data/lib/project_types/script/layers/infrastructure/languages/tool_version_checker.rb +26 -0
  45. data/lib/project_types/script/layers/infrastructure/languages/typescript_project_creator.rb +22 -10
  46. data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +30 -19
  47. data/lib/project_types/script/layers/infrastructure/languages/wasm_project_creator.rb +0 -3
  48. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +4 -0
  49. data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +13 -25
  50. data/lib/project_types/script/layers/infrastructure/script_service.rb +4 -2
  51. data/lib/project_types/script/loaders/project.rb +8 -7
  52. data/lib/project_types/script/messages/messages.rb +20 -19
  53. data/lib/project_types/script/ui/error_handler.rb +17 -4
  54. data/lib/project_types/script/ui/strict_spinner.rb +4 -6
  55. data/lib/project_types/theme/cli.rb +2 -0
  56. data/lib/project_types/theme/commands/common/root_helper.rb +71 -0
  57. data/lib/project_types/theme/commands/init.rb +2 -0
  58. data/lib/project_types/theme/commands/list.rb +34 -0
  59. data/lib/project_types/theme/commands/open.rb +65 -0
  60. data/lib/project_types/theme/commands/pull.rb +20 -10
  61. data/lib/project_types/theme/commands/push.rb +18 -12
  62. data/lib/project_types/theme/commands/serve.rb +6 -2
  63. data/lib/project_types/theme/conversions/base_glob.rb +50 -0
  64. data/lib/project_types/theme/conversions/ignore_glob.rb +15 -0
  65. data/lib/project_types/theme/conversions/include_glob.rb +15 -0
  66. data/lib/project_types/theme/forms/select.rb +11 -39
  67. data/lib/project_types/theme/messages/messages.rb +36 -7
  68. data/lib/project_types/theme/presenters/theme_presenter.rb +48 -0
  69. data/lib/project_types/theme/presenters/themes_presenter.rb +32 -0
  70. data/lib/shopify_cli/api.rb +1 -1
  71. data/lib/shopify_cli/command.rb +3 -1
  72. data/lib/shopify_cli/commands/app/create/node.rb +1 -0
  73. data/lib/shopify_cli/commands/app/create/php.rb +1 -0
  74. data/lib/shopify_cli/commands/app/create/rails.rb +1 -0
  75. data/lib/shopify_cli/commands/app/deploy.rb +1 -0
  76. data/lib/shopify_cli/constants.rb +3 -3
  77. data/lib/shopify_cli/context.rb +11 -13
  78. data/lib/shopify_cli/environment.rb +6 -0
  79. data/lib/shopify_cli/git.rb +9 -1
  80. data/lib/shopify_cli/messages/messages.rb +23 -3
  81. data/lib/shopify_cli/services/app/create/rails_service.rb +1 -1
  82. data/lib/shopify_cli/tasks/ensure_git_dependency.rb +14 -0
  83. data/lib/shopify_cli/tasks.rb +1 -0
  84. data/lib/shopify_cli/theme/dev_server/proxy.rb +14 -2
  85. data/lib/shopify_cli/theme/dev_server.rb +1 -3
  86. data/lib/shopify_cli/theme/development_theme.rb +11 -0
  87. data/lib/shopify_cli/theme/include_filter.rb +4 -2
  88. data/lib/shopify_cli/theme/syncer.rb +20 -11
  89. data/lib/shopify_cli/theme/theme.rb +0 -4
  90. data/lib/shopify_cli/version.rb +1 -1
  91. data/shopify-dev +9 -11
  92. metadata +16 -2
@@ -15,6 +15,8 @@ module Script
15
15
  id: build_file_path,
16
16
  uuid: script_project.uuid,
17
17
  extension_point_type: script_project.extension_point_type,
18
+ title: script_project.title,
19
+ description: script_project.description,
18
20
  script_content: script_content,
19
21
  metadata: metadata,
20
22
  script_config: script_project.script_config,
@@ -31,6 +33,8 @@ module Script
31
33
  id: build_file_path,
32
34
  uuid: script_project.uuid,
33
35
  extension_point_type: script_project.extension_point_type,
36
+ title: script_project.title,
37
+ description: script_project.description,
34
38
  script_content: script_content,
35
39
  metadata: metadata,
36
40
  script_config: script_project.script_config,
@@ -27,7 +27,7 @@ module Script
27
27
  change_directory(directory: initial_directory)
28
28
  end
29
29
 
30
- def create(script_name:, extension_point_type:, language:)
30
+ def create(title:, extension_point_type:, language:)
31
31
  validate_metadata!(extension_point_type, language)
32
32
 
33
33
  ShopifyCLI::Project.write(
@@ -35,7 +35,8 @@ module Script
35
35
  project_type: :script,
36
36
  organization_id: nil,
37
37
  extension_point_type: extension_point_type,
38
- script_name: script_name,
38
+ title: title,
39
+ description: nil,
39
40
  language: language
40
41
  )
41
42
 
@@ -48,7 +49,8 @@ module Script
48
49
  Domain::ScriptProject.new(
49
50
  id: project.directory,
50
51
  env: project.env,
51
- script_name: script_name,
52
+ title: title,
53
+ description: description,
52
54
  extension_point_type: extension_point_type,
53
55
  language: language,
54
56
  script_config: script_config_repository.get!,
@@ -79,11 +81,6 @@ module Script
79
81
  build_script_project
80
82
  end
81
83
 
82
- def update_script_config(title:)
83
- script_config = script_config_repository.update!(title: title)
84
- build_script_project(script_config: script_config)
85
- end
86
-
87
84
  private
88
85
 
89
86
  def build_script_project(
@@ -92,7 +89,8 @@ module Script
92
89
  Domain::ScriptProject.new(
93
90
  id: ctx.root,
94
91
  env: project.env,
95
- script_name: script_name,
92
+ title: title,
93
+ description: description,
96
94
  extension_point_type: extension_point_type,
97
95
  language: language,
98
96
  script_config: script_config,
@@ -111,8 +109,12 @@ module Script
111
109
  project_config_value!("extension_point_type")
112
110
  end
113
111
 
114
- def script_name
115
- project_config_value!("script_name")
112
+ def title
113
+ project_config_value!("title")
114
+ end
115
+
116
+ def description
117
+ project_config_value("description")
116
118
  end
117
119
 
118
120
  def language
@@ -181,26 +183,12 @@ module Script
181
183
  from_h(hash)
182
184
  end
183
185
 
184
- def update!(title:)
185
- hash = get!.content
186
- update_hash(hash: hash, title: title)
187
-
188
- ctx.write(filename, hash_to_file_content(hash))
189
-
190
- from_h(hash)
191
- end
192
-
193
186
  def filename
194
187
  raise NotImplementedError
195
188
  end
196
189
 
197
190
  private
198
191
 
199
- def update_hash(hash:, title:)
200
- hash["version"] ||= "2"
201
- hash["title"] = title
202
- end
203
-
204
192
  def from_h(hash)
205
193
  Domain::ScriptConfig.new(content: hash, filename: filename)
206
194
  end
@@ -15,6 +15,8 @@ module Script
15
15
  def set_app_script(
16
16
  uuid:,
17
17
  extension_point_type:,
18
+ title:,
19
+ description:,
18
20
  force: false,
19
21
  metadata:,
20
22
  script_config:,
@@ -26,8 +28,8 @@ module Script
26
28
  variables = {
27
29
  uuid: uuid,
28
30
  extensionPointName: extension_point_type.upcase,
29
- title: script_config.title,
30
- description: script_config.description,
31
+ title: title,
32
+ description: description,
31
33
  force: force,
32
34
  schemaMajorVersion: metadata.schema_major_version.to_s, # API expects string value
33
35
  schemaMinorVersion: metadata.schema_minor_version.to_s, # API expects string value
@@ -20,21 +20,22 @@ module Script
20
20
  project.env = env
21
21
  project
22
22
  rescue SmartProperties::InitializationError, SmartProperties::InvalidValueError => error
23
- handle_error(error, context: context, env_file_present: env_file_present)
23
+ handle_error(error, context: context)
24
24
  end
25
25
 
26
- def self.handle_error(error, context:, env_file_present:)
27
- if env_file_present
26
+ def self.handle_error(error, context:)
27
+ if ShopifyCLI::Environment.interactive?
28
28
  properties_hash = { api_key: "SHOPIFY_API_KEY", secret: "SHOPIFY_API_SECRET" }
29
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)
30
+ message = context.message("script.error.missing_env_file_variables", missing_env_variables)
31
+ message += context.message("script.error.missing_env_file_variables_solution", ShopifyCLI::TOOL_NAME)
32
32
  else
33
33
  properties_hash = { api_key: "--api-key", secret: "--api-secret" }
34
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
- ShopifyCli::TOOL_NAME)
35
+ message = context.message("script.error.missing_push_options_ci", missing_options)
36
+ message += context.message("script.error.missing_push_options_ci_solution", ShopifyCLI::TOOL_NAME)
37
37
  end
38
+ raise ShopifyCLI::Abort, message
38
39
  end
39
40
 
40
41
  def self.env_file_exists?(directory)
@@ -24,11 +24,11 @@ module Script
24
24
  oauth_help: "Wait a few minutes and try again.",
25
25
 
26
26
  invalid_context_cause: "Your .shopify-cli.yml is formatted incorrectly. It's missing values for "\
27
- "extension_point_type or script_name.",
27
+ "extension_point_type or title.",
28
28
  invalid_context_help: "Add these values.",
29
29
 
30
- invalid_script_name_cause: "Script name contains unsupported characters.",
31
- invalid_script_name_help: "Use only numbers, letters, hyphens, or underscores.",
30
+ invalid_script_title_cause: "Script title contains unsupported characters.",
31
+ invalid_script_title_help: "Use only numbers, letters, hyphens, or underscores.",
32
32
 
33
33
  no_existing_apps_cause: "Your script can't be pushed to an app because your Partner account "\
34
34
  "doesn't have any apps.",
@@ -37,8 +37,8 @@ module Script
37
37
  no_existing_orgs_cause: "Your account doesn't belong to a Partner Organization.",
38
38
  no_existing_orgs_help: "Visit https://partners.shopify.com/ to create an account.",
39
39
 
40
- project_exists_cause: "A directory with this same name already exists.",
41
- project_exists_help: "Choose a different name for your script.",
40
+ project_exists_cause: "A directory with this same title already exists.",
41
+ project_exists_help: "Choose a different title for your script.",
42
42
 
43
43
  invalid_extension_cause: "The name of the Script API is incorrect: %s.",
44
44
  invalid_extension_help: "Choose a supported API: %s.",
@@ -113,6 +113,10 @@ module Script
113
113
  dependency_install_cause: "Something went wrong while installing the needed dependencies.",
114
114
  dependency_install_help: "Correct the errors.",
115
115
 
116
+ invalid_environment_cause: "Your environment %{tool} version, %{env_version}, "\
117
+ "is too low. It must be at least %{minimum_version}.",
118
+ invalid_environment_help: "Update %{tool}.",
119
+
116
120
  failed_api_request_cause: "Something went wrong while communicating with Shopify.",
117
121
  failed_api_request_help: "Try again.",
118
122
 
@@ -150,11 +154,13 @@ module Script
150
154
  language_library_for_api_not_found_cause: "Script can’t be pushed because the %{language} library for API %{api} is missing.",
151
155
  language_library_for_api_not_found_help: "Make sure extension_point.yml contains the correct API library.",
152
156
  no_scripts_found_in_app: "The selected apps have no scripts. Please, create them first on the partners' dashboard.",
153
- missing_env_file_variables: "The following are missing in the .env file: %s."\
154
- " To add it, run {{command:%s script connect}}",
155
- missing_push_options: "The following are missing: %s. "\
156
- "To add them to a CI environment:\n\t1. Run a connect command {{command:%s script connect}}\n\t2. Navigate to the .env file at the root of your project\n\t"\
157
- "3. Copy the missing values, then pass them through as arguments.",
157
+ missing_push_options_ci: "The following are missing: %s. ",
158
+ missing_push_options_ci_solution: "To add them to a CI environment:\n\t1. Run a connect command " \
159
+ "({{command:%1$s script connect}})\n\t2. Navigate to the .env file at the root of your project\n\t" \
160
+ "3. Copy the missing values and pass them through as arguments in {{command:%1$s script push}}",
161
+ missing_env_file_variables: "The following are missing in the .env file: %s. ",
162
+ missing_env_file_variables_solution: "To add it, connect your script with " \
163
+ "{{command:%1$s script connect}} ",
158
164
  },
159
165
 
160
166
  create: {
@@ -162,9 +168,9 @@ module Script
162
168
  {{command:%1$s script create}}: Creates a script project.
163
169
  Usage: {{command:%1$s script create}}
164
170
  Options:
165
- {{command:--name=NAME}} Script project name.
171
+ {{command:--title=TITLE}} Script project title.
166
172
  {{command:--api=TYPE}} Script API name. Supported values: %2$s.
167
- {{command:--language=LANGUAGE}} Programming language. Supported values: %3$s.
173
+ {{command:--language=LANGUAGE}} Programming language. Defaults to wasm. Supported values: %3$s.
168
174
  HELP
169
175
 
170
176
  error: {
@@ -191,9 +197,7 @@ module Script
191
197
  HELP
192
198
 
193
199
  error: {
194
- operation_failed_no_uuid: "UUID is required to push in a CI environment.",
195
- operation_failed_with_api_key: "Couldn't push script to app (API key: %{api_key}).",
196
- operation_failed_no_api_key: "Couldn't push script to app.",
200
+ operation_failed: "Couldn't push script to app.",
197
201
  },
198
202
 
199
203
  script_pushed: "{{v}} Script pushed to app (API key: %{api_key}).",
@@ -206,8 +210,6 @@ module Script
206
210
  HELP
207
211
  error: {
208
212
  operation_failed: "Couldn't connect script to app.",
209
- missing_env_file_variables: "The following variables are missing in the .env file: %s."\
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}}",
211
213
  },
212
214
  },
213
215
  javy: {
@@ -233,8 +235,7 @@ module Script
233
235
  forms: {
234
236
  create: {
235
237
  select_extension_point: "Which Script API do you want to use?",
236
- select_language: "Which language do you want to use?",
237
- script_name: "What do you want to name your script?",
238
+ script_title: "What do you want to title your script?",
238
239
  },
239
240
  },
240
241
 
@@ -59,10 +59,10 @@ module Script
59
59
  Script::Layers::Application::ExtensionPoints.languages(type: e.extension_point_type).join(", ")
60
60
  ),
61
61
  }
62
- when Errors::InvalidScriptNameError
62
+ when Errors::InvalidScriptTitleError
63
63
  {
64
- cause_of_error: ShopifyCLI::Context.message("script.error.invalid_script_name_cause"),
65
- help_suggestion: ShopifyCLI::Context.message("script.error.invalid_script_name_help"),
64
+ cause_of_error: ShopifyCLI::Context.message("script.error.invalid_script_title_cause"),
65
+ help_suggestion: ShopifyCLI::Context.message("script.error.invalid_script_title_help"),
66
66
  }
67
67
  when Errors::NoExistingAppsError
68
68
  {
@@ -96,7 +96,7 @@ module Script
96
96
  {
97
97
  cause_of_error: ShopifyCLI::Context.message(
98
98
  "script.error.script_not_found_cause",
99
- e.script_name,
99
+ e.title,
100
100
  e.extension_point_type
101
101
  ),
102
102
  }
@@ -237,6 +237,19 @@ module Script
237
237
  ),
238
238
  help_suggestion: ShopifyCLI::Context.message("script.error.graphql_error_help"),
239
239
  }
240
+ when Layers::Infrastructure::Errors::InvalidEnvironmentError
241
+ {
242
+ cause_of_error: ShopifyCLI::Context.message(
243
+ "script.error.invalid_environment_cause",
244
+ tool: e.tool,
245
+ env_version: e.env_version,
246
+ minimum_version: e.minimum_version,
247
+ ),
248
+ help_suggestion: ShopifyCLI::Context.message(
249
+ "script.error.invalid_environment_help",
250
+ tool: e.tool,
251
+ ),
252
+ }
240
253
  when Layers::Infrastructure::Errors::SystemCallFailureError
241
254
  {
242
255
  cause_of_error: ShopifyCLI::Context
@@ -8,12 +8,10 @@ module Script
8
8
  def self.spin(title, auto_debrief: false)
9
9
  exception = nil
10
10
  CLI::UI::Spinner.spin(title, auto_debrief: auto_debrief) do |*args|
11
- begin
12
- yield(*args)
13
- rescue StandardError => e
14
- exception = e
15
- CLI::UI::Spinner::TASK_FAILED
16
- end
11
+ yield(*args)
12
+ rescue StandardError => e
13
+ exception = e
14
+ CLI::UI::Spinner::TASK_FAILED
17
15
  end
18
16
  raise exception if exception
19
17
  end
@@ -14,6 +14,8 @@ module Theme
14
14
  subcommand :Check, "check", Project.project_filepath("commands/check")
15
15
  subcommand :Publish, "publish", Project.project_filepath("commands/publish")
16
16
  subcommand :Package, "package", Project.project_filepath("commands/package")
17
+ subcommand :Open, "open", Project.project_filepath("commands/open")
18
+ subcommand :List, "list", Project.project_filepath("commands/list")
17
19
  subcommand :LanguageServer, "language-server", Project.project_filepath("commands/language_server")
18
20
  end
19
21
  ShopifyCLI::Commands.register("Theme::Command", "theme")
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Theme
4
+ class Command
5
+ module Common
6
+ module RootHelper
7
+ def root_value(options, name)
8
+ argv = default_argv(options)
9
+ command_index = argv.index(name.to_s)
10
+
11
+ return "." if command_index.nil?
12
+
13
+ next_index = command_index + 1
14
+ option_by_key = options_map(options)
15
+
16
+ while next_index < argv.size
17
+ element = argv[next_index]
18
+ key, value = key_value_tuple(element)
19
+ option = option_by_key[key]
20
+
21
+ return element if option.nil?
22
+
23
+ # Skip the option argument
24
+ next_index += 1 if !option.arg.nil? && !value
25
+
26
+ # PATTERN arguments take precedence over the `root`
27
+ if option.arg =~ /PATTERN/ && !value
28
+ next_index += 1 while option_argument?(argv, next_index, option_by_key)
29
+ next
30
+ end
31
+
32
+ next_index += 1
33
+ end
34
+
35
+ "."
36
+ end
37
+
38
+ private
39
+
40
+ def default_argv(options)
41
+ options.parser.default_argv.compact
42
+ end
43
+
44
+ def options_map(options)
45
+ map = {}
46
+ options_list(options).each do |option|
47
+ map[option.short.first] = option
48
+ map[option.long.first] = option
49
+ end
50
+ map
51
+ end
52
+
53
+ def options_list(options)
54
+ options.parser.top.list
55
+ end
56
+
57
+ def option_argument?(argv, next_index, option_by_key)
58
+ return false unless next_index < argv.size
59
+
60
+ element = argv[next_index]
61
+ key, _value = key_value_tuple(element)
62
+ option_by_key[key].nil?
63
+ end
64
+
65
+ def key_value_tuple(element)
66
+ element.split("=")
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -9,6 +9,8 @@ module Theme
9
9
  parser.on("-u", "--clone-url URL") { |url| flags[:clone_url] = url }
10
10
  end
11
11
 
12
+ prerequisite_task :ensure_git_dependency
13
+
12
14
  DEFAULT_CLONE_URL = "https://github.com/Shopify/dawn.git"
13
15
 
14
16
  def call(args, _name)
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shopify_cli/theme/theme"
4
+ require "project_types/theme/presenters/themes_presenter"
5
+
6
+ module Theme
7
+ class Command
8
+ class List < ShopifyCLI::Command::SubCommand
9
+ recommend_default_ruby_range
10
+
11
+ def call(_args, _name)
12
+ @ctx.puts(@ctx.message("theme.list.title", shop))
13
+
14
+ themes_presenter.all.each do |theme|
15
+ @ctx.puts(" #{theme}")
16
+ end
17
+ end
18
+
19
+ def self.help
20
+ @ctx.message("theme.list.help", ShopifyCLI::TOOL_NAME, ShopifyCLI::TOOL_NAME)
21
+ end
22
+
23
+ private
24
+
25
+ def themes_presenter
26
+ Theme::Presenters::ThemesPresenter.new(@ctx, nil)
27
+ end
28
+
29
+ def shop
30
+ ShopifyCLI::AdminAPI.get_shop_or_abort(@ctx)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shopify_cli/theme/theme"
4
+ require "shopify_cli/theme/development_theme"
5
+
6
+ module Theme
7
+ class Command
8
+ class Open < ShopifyCLI::Command::SubCommand
9
+ recommend_default_ruby_range
10
+
11
+ options do |parser, flags|
12
+ parser.on("-t", "--theme=NAME_OR_ID") { |theme| flags[:theme] = theme }
13
+ parser.on("-l", "--live") { flags[:live] = true }
14
+ parser.on("-d", "--development") { flags[:development] = true }
15
+ end
16
+
17
+ def call(_args, _name)
18
+ theme = find_theme(**options.flags)
19
+
20
+ @ctx.puts(@ctx.message("theme.open.details", theme.name, theme.editor_url))
21
+ @ctx.open_url!(theme.preview_url)
22
+ end
23
+
24
+ def self.help
25
+ ShopifyCLI::Context.message("theme.open.help", ShopifyCLI::TOOL_NAME, ShopifyCLI::TOOL_NAME)
26
+ end
27
+
28
+ def find_theme(theme: nil, live: nil, development: nil, **_args)
29
+ return theme_by_identifier(theme) if theme
30
+ return live_theme if live
31
+ return development_theme if development
32
+
33
+ select_theme
34
+ end
35
+
36
+ def theme_by_identifier(identifier)
37
+ theme = ShopifyCLI::Theme::Theme.find_by_identifier(@ctx, identifier: identifier)
38
+ theme || not_found_error(identifier)
39
+ end
40
+
41
+ def development_theme
42
+ theme = ShopifyCLI::Theme::DevelopmentTheme.find(@ctx)
43
+ theme || not_found_error("development")
44
+ end
45
+
46
+ def live_theme
47
+ ShopifyCLI::Theme::Theme.live(@ctx)
48
+ end
49
+
50
+ def not_found_error(identifier)
51
+ @ctx.abort(@ctx.message("theme.open.theme_not_found", identifier))
52
+ end
53
+
54
+ def select_theme
55
+ form = Forms::Select.ask(
56
+ @ctx,
57
+ [],
58
+ title: @ctx.message("theme.open.select"),
59
+ root: nil
60
+ )
61
+ form&.theme
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,43 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
  require "shopify_cli/theme/theme"
3
+ require "shopify_cli/theme/development_theme"
3
4
  require "shopify_cli/theme/ignore_filter"
4
5
  require "shopify_cli/theme/include_filter"
5
6
  require "shopify_cli/theme/syncer"
7
+ require "project_types/theme/commands/common/root_helper"
8
+ require "project_types/theme/conversions/include_glob"
9
+ require "project_types/theme/conversions/ignore_glob"
6
10
 
7
11
  module Theme
8
12
  class Command
9
13
  class Pull < ShopifyCLI::Command::SubCommand
14
+ include Common::RootHelper
15
+
10
16
  recommend_default_ruby_range
11
17
 
12
18
  options do |parser, flags|
19
+ Conversions::IncludeGlob.register(parser)
20
+ Conversions::IgnoreGlob.register(parser)
21
+
13
22
  parser.on("-n", "--nodelete") { flags[:nodelete] = true }
14
23
  parser.on("-i", "--themeid=ID") { |theme_id| flags[:theme_id] = theme_id }
15
24
  parser.on("-t", "--theme=NAME_OR_ID") { |theme| flags[:theme] = theme }
16
25
  parser.on("-l", "--live") { flags[:live] = true }
17
26
  parser.on("-d", "--development") { flags[:development] = true }
18
- parser.on("-o", "--only=PATTERN") do |pattern|
27
+ parser.on("-o", "--only=PATTERN", Conversions::IncludeGlob) do |pattern|
19
28
  flags[:includes] ||= []
20
- flags[:includes] << pattern
29
+ flags[:includes] += pattern
21
30
  end
22
- parser.on("-x", "--ignore=PATTERN") do |pattern|
31
+ parser.on("-x", "--ignore=PATTERN", Conversions::IgnoreGlob) do |pattern|
23
32
  flags[:ignores] ||= []
24
- flags[:ignores] << pattern
33
+ flags[:ignores] += pattern
25
34
  end
26
35
  end
27
36
 
28
- def call(args, _name)
29
- root = args.first || "."
37
+ def call(_args, name)
38
+ root = root_value(options, name)
30
39
  delete = !options.flags[:nodelete]
31
40
  theme = find_theme(root, **options.flags)
32
41
  return if theme.nil?
33
42
 
34
- include_filter = ShopifyCLI::Theme::IncludeFilter.new(options.flags[:includes])
43
+ include_filter = ShopifyCLI::Theme::IncludeFilter.new(root, options.flags[:includes])
35
44
  ignore_filter = ShopifyCLI::Theme::IgnoreFilter.from_path(root)
36
45
  ignore_filter.add_patterns(options.flags[:ignores]) if options.flags[:ignores]
37
46
 
38
47
  syncer = ShopifyCLI::Theme::Syncer.new(@ctx, theme: theme,
39
- include_filter: include_filter,
40
- ignore_filter: ignore_filter)
48
+ include_filter: include_filter,
49
+ ignore_filter: ignore_filter)
41
50
  begin
42
51
  syncer.start_threads
43
52
  CLI::UI::Frame.open(@ctx.message("theme.pull.pulling", theme.name, theme.id, theme.shop)) do
@@ -73,7 +82,8 @@ module Theme
73
82
  end
74
83
 
75
84
  if development
76
- return ShopifyCLI::Theme::Theme.development(@ctx, root: root)
85
+ dev_theme = ShopifyCLI::Theme::DevelopmentTheme.find(@ctx, root: root)
86
+ return dev_theme || @ctx.abort(@ctx.message("theme.pull.theme_not_found", "development"))
77
87
  end
78
88
 
79
89
  select_theme(root)
@@ -4,13 +4,21 @@ require "shopify_cli/theme/development_theme"
4
4
  require "shopify_cli/theme/ignore_filter"
5
5
  require "shopify_cli/theme/include_filter"
6
6
  require "shopify_cli/theme/syncer"
7
+ require "project_types/theme/commands/common/root_helper"
8
+ require "project_types/theme/conversions/include_glob"
9
+ require "project_types/theme/conversions/ignore_glob"
7
10
 
8
11
  module Theme
9
12
  class Command
10
13
  class Push < ShopifyCLI::Command::SubCommand
14
+ include Common::RootHelper
15
+
11
16
  recommend_default_ruby_range
12
17
 
13
18
  options do |parser, flags|
19
+ Conversions::IncludeGlob.register(parser)
20
+ Conversions::IgnoreGlob.register(parser)
21
+
14
22
  parser.on("-n", "--nodelete") { flags[:nodelete] = true }
15
23
  parser.on("-i", "--themeid=ID") { |theme_id| flags[:theme_id] = theme_id }
16
24
  parser.on("-t", "--theme=NAME_OR_ID") { |theme| flags[:theme] = theme }
@@ -20,18 +28,18 @@ module Theme
20
28
  parser.on("-j", "--json") { flags[:json] = true }
21
29
  parser.on("-a", "--allow-live") { flags[:allow_live] = true }
22
30
  parser.on("-p", "--publish") { flags[:publish] = true }
23
- parser.on("-o", "--only=PATTERN") do |pattern|
31
+ parser.on("-o", "--only=PATTERN", Conversions::IncludeGlob) do |pattern|
24
32
  flags[:includes] ||= []
25
- flags[:includes] << pattern
33
+ flags[:includes] += pattern
26
34
  end
27
- parser.on("-x", "--ignore=PATTERN") do |pattern|
35
+ parser.on("-x", "--ignore=PATTERN", Conversions::IgnoreGlob) do |pattern|
28
36
  flags[:ignores] ||= []
29
- flags[:ignores] << pattern
37
+ flags[:ignores] += pattern
30
38
  end
31
39
  end
32
40
 
33
- def call(args, _name)
34
- root = args.first || "."
41
+ def call(_args, name)
42
+ root = root_value(options, name)
35
43
  delete = !options.flags[:nodelete]
36
44
  theme = find_theme(root, **options.flags)
37
45
  return if theme.nil?
@@ -42,13 +50,13 @@ module Theme
42
50
  return unless CLI::UI::Prompt.confirm(question)
43
51
  end
44
52
 
45
- include_filter = ShopifyCLI::Theme::IncludeFilter.new(options.flags[:includes])
53
+ include_filter = ShopifyCLI::Theme::IncludeFilter.new(root, options.flags[:includes])
46
54
  ignore_filter = ShopifyCLI::Theme::IgnoreFilter.from_path(root)
47
55
  ignore_filter.add_patterns(options.flags[:ignores]) if options.flags[:ignores]
48
56
 
49
57
  syncer = ShopifyCLI::Theme::Syncer.new(@ctx, theme: theme,
50
- include_filter: include_filter,
51
- ignore_filter: ignore_filter)
58
+ include_filter: include_filter,
59
+ ignore_filter: ignore_filter)
52
60
  begin
53
61
  syncer.start_threads
54
62
  if options.flags[:json]
@@ -91,9 +99,7 @@ module Theme
91
99
  end
92
100
 
93
101
  if development
94
- new_theme = ShopifyCLI::Theme::DevelopmentTheme.new(@ctx, root: root)
95
- new_theme.ensure_exists!
96
- return new_theme
102
+ return ShopifyCLI::Theme::DevelopmentTheme.find_or_create!(@ctx, root: root)
97
103
  end
98
104
 
99
105
  if unpublished