shopify-cli 2.11.1 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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