shopify-cli 2.11.2 → 2.12.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -1
  3. data/Gemfile.lock +1 -1
  4. data/bin/shopify +8 -4
  5. data/docs/users/installation.md +1 -44
  6. data/lib/project_types/extension/models/specification_handlers/checkout_ui_extension.rb +114 -0
  7. data/lib/project_types/script/cli.rb +2 -0
  8. data/lib/project_types/script/commands/create.rb +2 -2
  9. data/lib/project_types/script/commands/push.rb +4 -6
  10. data/lib/project_types/script/config/extension_points.yml +0 -4
  11. data/lib/project_types/script/forms/create.rb +1 -14
  12. data/lib/project_types/script/layers/application/connect_app.rb +3 -2
  13. data/lib/project_types/script/layers/infrastructure/errors.rb +11 -0
  14. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_project_creator.rb +2 -6
  15. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +29 -18
  16. data/lib/project_types/script/layers/infrastructure/languages/tool_version_checker.rb +26 -0
  17. data/lib/project_types/script/layers/infrastructure/languages/typescript_project_creator.rb +3 -6
  18. data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +30 -19
  19. data/lib/project_types/script/loaders/project.rb +8 -7
  20. data/lib/project_types/script/messages/messages.rb +13 -12
  21. data/lib/project_types/script/ui/error_handler.rb +13 -0
  22. data/lib/project_types/theme/commands/common/root_helper.rb +65 -0
  23. data/lib/project_types/theme/commands/init.rb +2 -0
  24. data/lib/project_types/theme/commands/pull.rb +16 -8
  25. data/lib/project_types/theme/commands/push.rb +15 -7
  26. data/lib/project_types/theme/commands/serve.rb +6 -2
  27. data/lib/project_types/theme/conversions/base_glob.rb +50 -0
  28. data/lib/project_types/theme/conversions/ignore_glob.rb +15 -0
  29. data/lib/project_types/theme/conversions/include_glob.rb +15 -0
  30. data/lib/project_types/theme/messages/messages.rb +5 -5
  31. data/lib/shopify_cli/commands/app/create/node.rb +1 -0
  32. data/lib/shopify_cli/commands/app/create/php.rb +1 -0
  33. data/lib/shopify_cli/commands/app/create/rails.rb +1 -0
  34. data/lib/shopify_cli/commands/app/deploy.rb +1 -0
  35. data/lib/shopify_cli/environment.rb +6 -0
  36. data/lib/shopify_cli/git.rb +9 -1
  37. data/lib/shopify_cli/messages/messages.rb +21 -1
  38. data/lib/shopify_cli/tasks/ensure_git_dependency.rb +14 -0
  39. data/lib/shopify_cli/tasks.rb +1 -0
  40. data/lib/shopify_cli/theme/dev_server/proxy.rb +14 -2
  41. data/lib/shopify_cli/theme/include_filter.rb +4 -2
  42. data/lib/shopify_cli/theme/syncer.rb +13 -4
  43. data/lib/shopify_cli/version.rb +1 -1
  44. metadata +8 -2
@@ -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)
@@ -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: {
@@ -164,7 +170,7 @@ module Script
164
170
  Options:
165
171
  {{command:--name=NAME}} Script project name.
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,7 +235,6 @@ 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
238
  script_name: "What do you want to name your script?",
238
239
  },
239
240
  },
@@ -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
@@ -0,0 +1,65 @@
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
+ option = option_by_key[element]
19
+
20
+ return element if option.nil?
21
+
22
+ # Skip the option argument
23
+ next_index += 1 unless option.arg.nil?
24
+
25
+ # PATTERN arguments take precedence over the `root`
26
+ if option.arg =~ /PATTERN/
27
+ next_index += 1 while option_argument?(argv, next_index, option_by_key)
28
+ next
29
+ end
30
+
31
+ next_index += 1
32
+ end
33
+
34
+ "."
35
+ end
36
+
37
+ private
38
+
39
+ def default_argv(options)
40
+ options.parser.default_argv
41
+ end
42
+
43
+ def options_map(options)
44
+ map = {}
45
+ options_list(options).each do |option|
46
+ map[option.short.first] = option
47
+ map[option.long.first] = option
48
+ end
49
+ map
50
+ end
51
+
52
+ def options_list(options)
53
+ options.parser.top.list
54
+ end
55
+
56
+ def option_argument?(argv, next_index, option_by_key)
57
+ return false unless next_index < argv.size
58
+
59
+ element = argv[next_index]
60
+ option_by_key[element].nil?
61
+ end
62
+ end
63
+ end
64
+ end
65
+ 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)
@@ -4,35 +4,43 @@ 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 Pull < 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 }
17
25
  parser.on("-l", "--live") { flags[:live] = true }
18
26
  parser.on("-d", "--development") { flags[:development] = true }
19
- parser.on("-o", "--only=PATTERN") do |pattern|
27
+ parser.on("-o", "--only=PATTERN", Conversions::IncludeGlob) do |pattern|
20
28
  flags[:includes] ||= []
21
- flags[:includes] << pattern
29
+ flags[:includes] += pattern
22
30
  end
23
- parser.on("-x", "--ignore=PATTERN") do |pattern|
31
+ parser.on("-x", "--ignore=PATTERN", Conversions::IgnoreGlob) do |pattern|
24
32
  flags[:ignores] ||= []
25
- flags[:ignores] << pattern
33
+ flags[:ignores] += pattern
26
34
  end
27
35
  end
28
36
 
29
- def call(args, _name)
30
- root = args.first || "."
37
+ def call(_args, name)
38
+ root = root_value(options, name)
31
39
  delete = !options.flags[:nodelete]
32
40
  theme = find_theme(root, **options.flags)
33
41
  return if theme.nil?
34
42
 
35
- include_filter = ShopifyCLI::Theme::IncludeFilter.new(options.flags[:includes])
43
+ include_filter = ShopifyCLI::Theme::IncludeFilter.new(root, options.flags[:includes])
36
44
  ignore_filter = ShopifyCLI::Theme::IgnoreFilter.from_path(root)
37
45
  ignore_filter.add_patterns(options.flags[:ignores]) if options.flags[:ignores]
38
46
 
@@ -75,7 +83,7 @@ module Theme
75
83
 
76
84
  if development
77
85
  dev_theme = ShopifyCLI::Theme::DevelopmentTheme.find(@ctx, root: root)
78
- return dev_theme || @ctx.abort(@ctx.message("theme.pull.theme_not_found", dev_theme.name))
86
+ return dev_theme || @ctx.abort(@ctx.message("theme.pull.theme_not_found", "development"))
79
87
  end
80
88
 
81
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,7 +50,7 @@ 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
 
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
  require "shopify_cli/theme/dev_server"
3
+ require "project_types/theme/commands/common/root_helper"
3
4
 
4
5
  module Theme
5
6
  class Command
6
7
  class Serve < ShopifyCLI::Command::SubCommand
8
+ include Common::RootHelper
9
+
7
10
  recommend_default_ruby_range
8
11
 
9
12
  DEFAULT_HTTP_HOST = "127.0.0.1"
@@ -15,10 +18,11 @@ module Theme
15
18
  parser.on("--live-reload=MODE") { |mode| flags[:mode] = as_reload_mode(mode) }
16
19
  end
17
20
 
18
- def call(*)
21
+ def call(_args, name)
22
+ root = root_value(options, name)
19
23
  flags = options.flags.dup
20
24
  host = flags[:host] || DEFAULT_HTTP_HOST
21
- ShopifyCLI::Theme::DevServer.start(@ctx, ".", host: host, **flags) do |syncer|
25
+ ShopifyCLI::Theme::DevServer.start(@ctx, root, host: host, **flags) do |syncer|
22
26
  UI::SyncProgressBar.new(syncer).progress(:upload_theme!, delay_low_priority_files: true)
23
27
  end
24
28
  rescue ShopifyCLI::Theme::DevServer::AddressBindingError
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Theme
4
+ module Conversions
5
+ class BaseGlob
6
+ class << self
7
+ def register(parser)
8
+ parser.accept(self) { |_val| convert(parser) }
9
+ end
10
+
11
+ def convert(parser)
12
+ argv = parser.default_argv
13
+ option_index = argv.index { |v| options.include?(v) }
14
+
15
+ return [] if option_index.nil?
16
+
17
+ start_index = option_index + 1
18
+ option_by_key = options_map(parser)
19
+ values = []
20
+
21
+ argv[start_index..-1].each do |value|
22
+ return values unless option_by_key[value].nil?
23
+ values << value
24
+ end
25
+
26
+ values
27
+ end
28
+
29
+ def options
30
+ raise "`#{self.class.name}#options` must be defined"
31
+ end
32
+
33
+ private
34
+
35
+ def options_map(parser)
36
+ map = {}
37
+ parser.top.list.each do |option|
38
+ map[option.short.first] = option
39
+ map[option.long.first] = option
40
+ end
41
+ map
42
+ end
43
+
44
+ def parameter?(value)
45
+ value.start_with?("-")
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_glob"
4
+
5
+ module Theme
6
+ module Conversions
7
+ class IgnoreGlob < BaseGlob
8
+ class << self
9
+ def options
10
+ %w(-x --ignore)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_glob"
4
+
5
+ module Theme
6
+ module Conversions
7
+ class IncludeGlob < BaseGlob
8
+ class << self
9
+ def options
10
+ %w(-o --only)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -65,8 +65,8 @@ module Theme
65
65
  {{command:-j, --json}} Output JSON instead of a UI.
66
66
  {{command:-a, --allow-live}} Allow push to a live theme.
67
67
  {{command:-p, --publish}} Publish as the live theme after uploading.
68
- {{command:-o, --only}} Upload only the specified files.
69
- {{command:-x, --ignore}} Skip uploading the specified files.
68
+ {{command:-o, --only}} Upload only the specified files (Multiple flags allowed).
69
+ {{command:-x, --ignore}} Skip uploading the specified files (Multiple flags allowed).
70
70
 
71
71
  Run without options to select theme from a list.
72
72
  HELP
@@ -96,7 +96,7 @@ module Theme
96
96
  help: <<~HELP,
97
97
  Uploads the current theme as a development theme to the connected store, then prints theme editor and preview URLs to your terminal. While running, changes will push to the store in real time.
98
98
 
99
- Usage: {{command:%s theme serve}}
99
+ Usage: {{command:%s theme serve [ ROOT ]}}
100
100
 
101
101
  Options:
102
102
  {{command:--port=PORT}} Local port to serve theme preview from.
@@ -202,8 +202,8 @@ module Theme
202
202
  {{command:-l, --live}} Pull theme files from your remote live theme.
203
203
  {{command:-d, --development}} Pull theme files from your remote development theme.
204
204
  {{command:-n, --nodelete}} Runs the pull command without deleting local files.
205
- {{command:-o, --only}} Download only the specified files.
206
- {{command:-x, --ignore}} Skip downloading the specified files.
205
+ {{command:-o, --only}} Download only the specified files (Multiple flags allowed).
206
+ {{command:-x, --ignore}} Skip downloading the specified files (Multiple flags allowed).
207
207
 
208
208
  Run without options to select theme from a list.
209
209
  HELP
@@ -4,6 +4,7 @@ module ShopifyCLI
4
4
  class Create
5
5
  class Node < ShopifyCLI::Command::AppSubCommand
6
6
  prerequisite_task :ensure_authenticated
7
+ prerequisite_task :ensure_git_dependency
7
8
 
8
9
  recommend_default_node_range
9
10
  recommend_default_ruby_range
@@ -4,6 +4,7 @@ module ShopifyCLI
4
4
  class Create
5
5
  class PHP < ShopifyCLI::Command::AppSubCommand
6
6
  prerequisite_task :ensure_authenticated
7
+ prerequisite_task :ensure_git_dependency
7
8
 
8
9
  options do |parser, flags|
9
10
  parser.on("--name=NAME") { |name| flags[:name] = name }
@@ -4,6 +4,7 @@ module ShopifyCLI
4
4
  class Create
5
5
  class Rails < ShopifyCLI::Command::AppSubCommand
6
6
  prerequisite_task :ensure_authenticated
7
+ prerequisite_task :ensure_git_dependency
7
8
 
8
9
  recommend_default_ruby_range
9
10
  recommend_default_node_range
@@ -3,6 +3,7 @@ module ShopifyCLI
3
3
  class App
4
4
  class Deploy < ShopifyCLI::Command::AppSubCommand
5
5
  subcommand :Heroku, "heroku", "shopify_cli/commands/app/deploy/heroku"
6
+ prerequisite_task :ensure_git_dependency
6
7
 
7
8
  recommend_default_ruby_range
8
9
 
@@ -25,6 +25,12 @@ module ShopifyCLI
25
25
  ::Semantic::Version.new(out.chomp)
26
26
  end
27
27
 
28
+ def self.npm_version(context: Context.new)
29
+ out, err, stat = context.capture3("npm", "--version")
30
+ raise ShopifyCLI::Abort, err unless stat.success?
31
+ ::Semantic::Version.new(out.chomp)
32
+ end
33
+
28
34
  def self.interactive=(interactive)
29
35
  @interactive = interactive
30
36
  end
@@ -4,7 +4,15 @@ module ShopifyCLI
4
4
  # git.
5
5
  class Git
6
6
  class << self
7
- # Check if Git is available in the environment
7
+ # Check if Git exists in the environment
8
+ def exists?(ctx)
9
+ _output, status = ctx.capture2e("git", "version")
10
+ status.success?
11
+ rescue Errno::ENOENT # git is not installed
12
+ false
13
+ end
14
+
15
+ # Check if the current working directory is a Git repository
8
16
  def available?(ctx)
9
17
  _output, status = ctx.capture2e("git", "status")
10
18
  status.success?
@@ -277,6 +277,24 @@ module ShopifyCLI
277
277
  HELP
278
278
  },
279
279
  },
280
+ extension: {
281
+ push: {
282
+ checkout_ui_extension: {
283
+ localization: {
284
+ error: {
285
+ bundle_too_large: "Total size of all locale files must be less than %s.",
286
+ file_empty: "Locale file `%s` is empty.",
287
+ file_too_large: "Locale file `%s` too large; size must be less than %s.",
288
+ invalid_file_extension: "Invalid locale filename: `%s`; only .json files are allowed.",
289
+ invalid_locale_code: "Invalid locale filename: `%s`; locale code should be 2 or 3 letters,"\
290
+ " optionally followed by a two-letter region code, e.g. `fr-CA`.",
291
+ single_default_locale: "There must be one and only one locale identified as the default locale,"\
292
+ " e.g. `en.default.json`",
293
+ },
294
+ },
295
+ },
296
+ },
297
+ },
280
298
  error_reporting: {
281
299
  unhandled_error: {
282
300
  message: "{{x}} {{red:An unexpected error occured.}}",
@@ -355,6 +373,7 @@ module ShopifyCLI
355
373
  error: {
356
374
  directory_exists: "Project directory already exists. Please create a project with a new name.",
357
375
  no_branches_found: "Could not find any git branches",
376
+ nonexistent: "Git needs to be installed: https://git-scm.com/download",
358
377
  repo_not_initiated:
359
378
  "Git repo is not initiated. Please run {{command:git init}} and make at least one commit.",
360
379
  no_commits_made: "No git commits have been made. Please make at least one commit.",
@@ -672,7 +691,8 @@ module ShopifyCLI
672
691
  },
673
692
  },
674
693
  ensure_dev_store: {
675
- could_not_verify_store: "Couldn't verify your store %s",
694
+ could_not_verify_store: "Couldn't verify your store. If you don't have a development store set up, "\
695
+ "please create one in your Partners dashboard and run `shopify app connect`.",
676
696
  convert_to_dev_store: <<~MESSAGE,
677
697
  Do you want to convert %s to a development store?
678
698
  Doing this will allow you to install your app, but the store will become {{bold:transfer-disabled}}.
@@ -0,0 +1,14 @@
1
+ require "shopify_cli"
2
+
3
+ module ShopifyCLI
4
+ module Tasks
5
+ class EnsureGitDependency < ShopifyCLI::Task
6
+ def call(ctx)
7
+ return if ShopifyCLI::Environment.acceptance_test?
8
+ unless ShopifyCLI::Git.exists?(ctx)
9
+ raise ShopifyCLI::Abort, ctx.message("core.git.error.nonexistent")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -34,6 +34,7 @@ module ShopifyCLI
34
34
  register :CreateApiClient, :create_api_client, "shopify_cli/tasks/create_api_client"
35
35
  register :EnsureAuthenticated, :ensure_authenticated, "shopify_cli/tasks/ensure_authenticated"
36
36
  register :EnsureEnv, :ensure_env, "shopify_cli/tasks/ensure_env"
37
+ register :EnsureGitDependency, :ensure_git_dependency, "shopify_cli/tasks/ensure_git_dependency"
37
38
  register :EnsureLoopbackURL, :ensure_loopback_url, "shopify_cli/tasks/ensure_loopback_url"
38
39
  register :EnsureProjectType, :ensure_project_type, "shopify_cli/tasks/ensure_project_type"
39
40
  register :EnsureDevStore, :ensure_dev_store, "shopify_cli/tasks/ensure_dev_store"
@@ -3,7 +3,6 @@ require "net/http"
3
3
  require "stringio"
4
4
  require "time"
5
5
  require "cgi"
6
-
7
6
  require_relative "proxy/template_param_builder"
8
7
 
9
8
  module ShopifyCLI
@@ -70,13 +69,26 @@ module ShopifyCLI
70
69
  @core_endpoints << env["PATH_INFO"]
71
70
  end
72
71
 
73
- body = response.body || [""]
72
+ body = patch_body(env, response.body)
74
73
  body = [body] unless body.respond_to?(:each)
75
74
  [response.code, headers, body]
76
75
  end
77
76
 
78
77
  private
79
78
 
79
+ def patch_body(env, body)
80
+ return [""] unless body
81
+
82
+ body.gsub(%r{(data-.+=(["']))(http:|https:)?//#{@theme.shop}(.*)(\2)}) do |_|
83
+ match = Regexp.last_match
84
+ "#{match[1]}http://#{host(env)}#{match[4]}#{match[5]}"
85
+ end
86
+ end
87
+
88
+ def host(env)
89
+ env["HTTP_HOST"]
90
+ end
91
+
80
92
  def has_body?(headers)
81
93
  headers["Content-Length"] || headers["Transfer-Encoding"]
82
94
  end
@@ -9,7 +9,8 @@ module ShopifyCLI
9
9
 
10
10
  attr_reader :globs, :regexes
11
11
 
12
- def initialize(patterns = [])
12
+ def initialize(root, patterns = [])
13
+ @root = Pathname.new(root)
13
14
  @patterns = patterns.nil? ? [] : patterns.compact.reject(&:empty?)
14
15
 
15
16
  regexes, globs = patterns_to_regexes_and_globs(@patterns)
@@ -22,9 +23,10 @@ module ShopifyCLI
22
23
  return true unless present?(@patterns)
23
24
 
24
25
  path = path.to_s
25
-
26
26
  return true if path.empty?
27
27
 
28
+ path = @root.join(path).to_s
29
+
28
30
  regexes.each do |regex|
29
31
  return true if regex_match?(regex, path)
30
32
  end