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
@@ -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
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "project_types/theme/presenters/themes_presenter"
4
+
1
5
  module Theme
2
6
  module Forms
3
7
  class Select < ShopifyCLI::Form
@@ -6,53 +10,21 @@ module Theme
6
10
 
7
11
  def ask
8
12
  self.theme = CLI::UI::Prompt.ask(title, allow_empty: false) do |handler|
9
- themes.each do |theme|
13
+ theme_presenters.each do |presenter|
14
+ theme = presenter.theme
15
+
10
16
  next if exclude_roles&.include?(theme.role)
11
17
  next if !include_foreign_developments && theme.foreign_development?
12
- handler.option("#{theme.name} #{theme_tags(theme)}") { theme }
18
+
19
+ handler.option(presenter.to_s(:short)) { theme }
13
20
  end
14
21
  end
15
22
  end
16
23
 
17
24
  private
18
25
 
19
- def themes
20
- @themes ||= ShopifyCLI::Theme::Theme.all(@ctx, root: root)
21
- .sort_by { |theme| theme_sort_order(theme) }
22
- end
23
-
24
- def theme_sort_order(theme)
25
- case theme.role
26
- when "live"
27
- 0
28
- when "unpublished"
29
- 1
30
- when "development"
31
- 2
32
- else
33
- 3
34
- end
35
- end
36
-
37
- def theme_tags(theme)
38
- color = case theme.role
39
- when "live"
40
- "green"
41
- when "unpublished"
42
- "yellow"
43
- when "development"
44
- "blue"
45
- else
46
- "italic"
47
- end
48
-
49
- tags = ["{{#{color}:[#{theme.role}]}}"]
50
-
51
- if theme.current_development?
52
- tags << "{{cyan:[yours]}}}}"
53
- end
54
-
55
- tags.join(" ")
26
+ def theme_presenters
27
+ Theme::Presenters::ThemesPresenter.new(ctx, root).all
56
28
  end
57
29
  end
58
30
  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
@@ -87,7 +87,7 @@ module Theme
87
87
  {{info:View your theme:}}
88
88
  {{underline:%s}}
89
89
 
90
- {{info:Customize this theme in the Online Store Editor:}}
90
+ {{info:Customize this theme in the Theme Editor:}}
91
91
  {{underline:%s}}
92
92
  DONE
93
93
  name: "Theme name",
@@ -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.
@@ -130,7 +130,7 @@ module Theme
130
130
  SERVING
131
131
  customize_or_preview: <<~CUSTOMIZE_OR_PREVIEW,
132
132
 
133
- Customize this theme in the Online Store Editor:
133
+ Customize this theme in the Theme Editor:
134
134
  {{green:%s}}
135
135
 
136
136
  Share this theme preview:
@@ -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
@@ -215,6 +215,35 @@ module Theme
215
215
  WARN
216
216
  theme_not_found: "Theme \"%s\" doesn't exist",
217
217
  },
218
+ open: {
219
+ select: "Select a theme to open",
220
+ theme_not_found: "Theme \"%s\" doesn't exist",
221
+ details: <<~DETAILS,
222
+ {{*}} {{bold:%s}}
223
+
224
+ Customize your theme in the Theme Editor:
225
+ {{green:%s}}
226
+
227
+ DETAILS
228
+ help: <<~HELP,
229
+ {{command:%s theme open}}: Opens the preview of your remote theme.
230
+
231
+ Usage: {{command:%s theme open}}
232
+
233
+ Options:
234
+ {{command:-t, --theme=NAME_OR_ID}} Theme ID or name of your theme.
235
+ {{command:-l, --live}} Open your live theme.
236
+ {{command:-d, --development}} Open your development theme.
237
+ HELP
238
+ },
239
+ list: {
240
+ title: "{{*}} List of {{bold:%s}} themes:",
241
+ help: <<~HELP,
242
+ {{command:%s theme list}}: Lists your remote themes.
243
+
244
+ Usage: {{command:%s theme list}}
245
+ HELP
246
+ },
218
247
  },
219
248
  }.freeze
220
249
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module Theme
6
+ module Presenters
7
+ class ThemePresenter
8
+ extend Forwardable
9
+
10
+ COLOR_BY_ROLE = {
11
+ "live" => "green",
12
+ "unpublished" => "yellow",
13
+ "development" => "blue",
14
+ }
15
+
16
+ attr_reader :theme
17
+
18
+ def_delegators :theme, :id, :name, :role
19
+
20
+ def initialize(theme)
21
+ @theme = theme
22
+ end
23
+
24
+ def to_s(mode = :long)
25
+ case mode
26
+ when :short
27
+ "{{bold:#{name} #{theme_tags}}}"
28
+ when :long
29
+ "{{green:##{id}}} {{bold:#{name} #{theme_tags}}}"
30
+ else
31
+ inspect
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def theme_tags
38
+ tags = ["{{#{tag_color}:[#{role}]}}"]
39
+ tags << "{{cyan:[yours]}}}}" if theme.current_development?
40
+ tags.join(" ")
41
+ end
42
+
43
+ def tag_color
44
+ COLOR_BY_ROLE[role] || "italic"
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "theme_presenter"
4
+
5
+ module Theme
6
+ module Presenters
7
+ class ThemesPresenter
8
+ ORDER_BY_ROLE = %w(live unpublished development)
9
+
10
+ def initialize(ctx, root)
11
+ @ctx = ctx
12
+ @root = root
13
+ end
14
+
15
+ def all
16
+ all_themes
17
+ .sort_by { |theme| order_by_role(theme) }
18
+ .map { |theme| ThemePresenter.new(theme) }
19
+ end
20
+
21
+ private
22
+
23
+ def order_by_role(theme)
24
+ ORDER_BY_ROLE.index(theme.role) || ORDER_BY_ROLE.size
25
+ end
26
+
27
+ def all_themes
28
+ ShopifyCLI::Theme::Theme.all(@ctx, root: @root)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -85,7 +85,7 @@ module ShopifyCLI
85
85
  end
86
86
  rescue Errno::ETIMEDOUT, Timeout::Error
87
87
  ctx.debug("timeout in #{method} #{uri} with X-Request-Id: #{headers["X-Request-Id"]}")
88
- raise APIRequestTimeoutError.new("Timeout")
88
+ raise APIRequestTimeoutError, "Timeout"
89
89
  end.retry_after(APIRequestRetriableError, retries: 3) do |e|
90
90
  sleep(1) if e.is_a?(APIRequestThrottledError)
91
91
  end
@@ -100,8 +100,10 @@ module ShopifyCLI
100
100
  end
101
101
 
102
102
  def check_node_version
103
+ return unless @compatible_node_range
104
+
103
105
  context = Context.new
104
- if @compatible_node_range && context.which("node").nil?
106
+ if context.which("node").nil?
105
107
  raise ShopifyCLI::Abort, context.message("core.errors.missing_node")
106
108
  end
107
109
 
@@ -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
 
@@ -61,12 +61,12 @@ module ShopifyCLI
61
61
  module SupportedVersions
62
62
  module Ruby
63
63
  FROM = "2.6.6"
64
- TO = "3.1.0"
64
+ TO = "3.2.0"
65
65
  end
66
66
 
67
67
  module Node
68
- FROM = "12.0.0"
69
- TO = "17.0.0"
68
+ FROM = "14.5.0"
69
+ TO = "18.0.0"
70
70
  end
71
71
  end
72
72
 
@@ -570,21 +570,19 @@ module ShopifyCLI
570
570
  trap("INFO", "DEFAULT")
571
571
 
572
572
  fork do
573
- begin
574
- r, w = IO.pipe
573
+ r, w = IO.pipe
574
+ @signal = false
575
+ trap("SIGINFO") do
576
+ @signal = true
577
+ w.write(0)
578
+ end
579
+ while r.read(1)
580
+ next unless @signal
575
581
  @signal = false
576
- trap("SIGINFO") do
577
- @signal = true
578
- w.write(0)
579
- end
580
- while r.read(1)
581
- next unless @signal
582
- @signal = false
583
- yield
584
- end
585
- rescue Interrupt
586
- exit(0)
582
+ yield
587
583
  end
584
+ rescue Interrupt
585
+ exit(0)
588
586
  end
589
587
  end
590
588
 
@@ -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?
@@ -15,7 +15,7 @@ module ShopifyCLI
15
15
  },
16
16
  core: {
17
17
  errors: {
18
- missing_node: "Node is necessary for this command and was not found in the environment.",
18
+ missing_node: "Node is required to continue. Install node here: https://nodejs.org/en/download.",
19
19
  option_parser: {
20
20
  invalid_option: "The option {{command:%s}} is not supported.",
21
21
  missing_argument: "The required argument {{command:%s}} is missing.",
@@ -42,7 +42,7 @@ module ShopifyCLI
42
42
  invalid_type: "The type %s is not supported. The only supported types are"\
43
43
  " {{command:[ rails | node | php ]}}",
44
44
  help: <<~HELP,
45
- {{command:%s app create}}: Creates a ruby on rails app.
45
+ {{command:%s app create}}: Creates a new project in a subdirectory.
46
46
  Usage: {{command:%s app create [ rails | node | php ]}}
47
47
  HELP
48
48
  rails: {
@@ -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}}.
@@ -109,7 +109,7 @@ module ShopifyCLI
109
109
 
110
110
  def check_ruby
111
111
  ruby_version = Rails::Ruby.version(context)
112
- return if ruby_version.satisfies?("~>2.5") || ruby_version.satisfies?("~>3.0.0")
112
+ return if ruby_version.satisfies?("~>2.5") || ruby_version.satisfies?("~>3.1.0")
113
113
  context.abort(context.message("core.app.create.rails.error.invalid_ruby_version"))
114
114
  end
115
115
 
@@ -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
@@ -28,7 +28,7 @@ module ShopifyCLI
28
28
 
29
29
  def start(ctx, root, host: "127.0.0.1", port: 9292, poll: false, mode: ReloadMode.default)
30
30
  @ctx = ctx
31
- theme = DevelopmentTheme.new(ctx, root: root)
31
+ theme = DevelopmentTheme.find_or_create!(ctx, root: root)
32
32
  ignore_filter = IgnoreFilter.from_path(root)
33
33
  @syncer = Syncer.new(ctx, theme: theme, ignore_filter: ignore_filter)
34
34
  watcher = Watcher.new(ctx, theme: theme, syncer: @syncer, ignore_filter: ignore_filter, poll: poll)
@@ -41,8 +41,6 @@ module ShopifyCLI
41
41
  stopped = false
42
42
  address = "http://#{host}:#{port}"
43
43
 
44
- theme.ensure_exists!
45
-
46
44
  trap("INT") do
47
45
  stopped = true
48
46
  stop
@@ -38,6 +38,8 @@ module ShopifyCLI
38
38
  @ctx.debug("Created temporary development theme: #{@id}")
39
39
  ShopifyCLI::DB.set(development_theme_id: @id)
40
40
  end
41
+
42
+ self
41
43
  end
42
44
 
43
45
  def exists?
@@ -63,6 +65,15 @@ module ShopifyCLI
63
65
  new(ctx).delete
64
66
  end
65
67
 
68
+ def self.find(ctx, root: nil)
69
+ dev_theme = new(ctx, root: root)
70
+ dev_theme.exists? ? dev_theme : nil
71
+ end
72
+
73
+ def self.find_or_create!(ctx, root: nil)
74
+ new(ctx, root: root).ensure_exists!
75
+ end
76
+
66
77
  private
67
78
 
68
79
  def generate_theme_name