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
@@ -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