shopify-cli 2.11.0 → 2.11.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -1
  3. data/Gemfile.lock +1 -1
  4. data/lib/project_types/extension/commands/push.rb +13 -0
  5. data/lib/project_types/extension/loaders/project.rb +28 -8
  6. data/lib/project_types/extension/messages/messages.rb +10 -2
  7. data/lib/project_types/script/layers/application/create_script.rb +1 -1
  8. data/lib/project_types/script/layers/application/push_script.rb +1 -1
  9. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +1 -8
  10. data/lib/project_types/script/layers/infrastructure/languages/task_runner.rb +35 -9
  11. data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +1 -8
  12. data/lib/project_types/script/layers/infrastructure/languages/wasm_task_runner.rb +3 -7
  13. data/lib/project_types/script/messages/messages.rb +6 -6
  14. data/lib/project_types/theme/commands/check.rb +0 -1
  15. data/lib/project_types/theme/commands/delete.rb +0 -1
  16. data/lib/project_types/theme/commands/init.rb +0 -1
  17. data/lib/project_types/theme/commands/language_server.rb +0 -1
  18. data/lib/project_types/theme/commands/package.rb +0 -1
  19. data/lib/project_types/theme/commands/publish.rb +0 -1
  20. data/lib/project_types/theme/commands/pull.rb +0 -1
  21. data/lib/project_types/theme/commands/push.rb +0 -1
  22. data/lib/project_types/theme/commands/serve.rb +0 -1
  23. data/lib/shopify_cli/command.rb +9 -3
  24. data/lib/shopify_cli/commands/app/create/rails.rb +1 -1
  25. data/lib/shopify_cli/commands/app/create.rb +0 -3
  26. data/lib/shopify_cli/commands/app/deploy.rb +0 -1
  27. data/lib/shopify_cli/commands/app/serve.rb +0 -1
  28. data/lib/shopify_cli/constants.rb +1 -1
  29. data/lib/shopify_cli/environment.rb +39 -45
  30. data/lib/shopify_cli/messages/messages.rb +1 -0
  31. data/lib/shopify_cli/theme/dev_server/hot_reload/remote_file_reloader.rb +63 -0
  32. data/lib/shopify_cli/theme/dev_server/hot_reload.rb +22 -6
  33. data/lib/shopify_cli/theme/dev_server/proxy.rb +4 -5
  34. data/lib/shopify_cli/theme/file.rb +4 -0
  35. data/lib/shopify_cli/version.rb +1 -1
  36. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9944385de970a522f54fcf4e8feffa5bcab8ad61498095a1794c5abcf5918063
4
- data.tar.gz: 62b23eb1ab82cf0783ccab14d77b55c3e71595d87581795fff19227c4bcada57
3
+ metadata.gz: b4c9bb44a8b7b750acbf3364201705d93dfb39ce9772976ba2af04183cfc5748
4
+ data.tar.gz: fa32248af15c17d01d80132b4caf42ea224edba35146098954f51b4453db50de
5
5
  SHA512:
6
- metadata.gz: 72ec8ee4a02bb2b7cd6f9d58c094625bba0e1971a1d2e81d86920e8b326ff8fb52bac73b2a7afec43f7e67991b239bd7ac11c43a2df70790ecd82cf6c4614db2
7
- data.tar.gz: e456bb338bfa6102cf8643455b983a147a0fdef6fb820a5ee46dd210e6cbde788de6053727c48ab44057a7ad920a8b765651bfd09a26735b4db8525dc6f247b1
6
+ metadata.gz: 3446d1a595e343f6566b7ec087c187d31518398ad57be75ff519a394133c75e4f0a5f8670d78913271091632d8ef9fdeef4fb1bb3c1b4ac5cb99d0bc12aef765
7
+ data.tar.gz: 48ad3c64a367e80facb7aecc393295fb8fa838949a51c7a71310c50a40e6bf2c648b57dbcaf53ade5e15af90ff911593e38865cbb8e2b8dce6b70a8918d83a03
data/CHANGELOG.md CHANGED
@@ -2,8 +2,14 @@ From version 2.6.0, the sections in this file adhere to the [keep a changelog](h
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
- ## Version 2.11.0
5
+ ## Version 2.11.1
6
+ ### Fixed
7
+ * [#1973](https://github.com/Shopify/shopify-cli/pull/1973): Fix `theme serve` to preview generated files (`*.css.liquid`)
8
+ * [#2034](https://github.com/Shopify/shopify-cli/pull/2034): Fix `theme serve` to accept parameters with multiple values
9
+ * [#2033](https://github.com/Shopify/shopify-cli/pull/2033): Pin Homebrew Ruby to 3.0
10
+ * [#2032](https://github.com/Shopify/shopify-cli/pull/2032): Runtime error checking the Node version if Node is not present in the environment.
6
11
 
12
+ ## Version 2.11.0
7
13
  ### Fixed
8
14
  * [#2005](https://github.com/Shopify/shopify-cli/pull/2005): Fix PHP app serve on Windows environments
9
15
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shopify-cli (2.11.0)
4
+ shopify-cli (2.11.1)
5
5
  bugsnag (~> 6.22)
6
6
  listen (~> 3.7.0)
7
7
  theme-check (~> 1.9.0)
@@ -27,6 +27,10 @@ module Extension
27
27
  api_secret: options.flags[:api_secret],
28
28
  registration_id: options.flags[:registration_id]
29
29
  )
30
+ # on ci, registration id must be present
31
+ registration_id = options.flags[:registration_id]
32
+ check_registration(registration_id: registration_id, context: @ctx)
33
+
30
34
  specification_handler = Extension::Loaders::SpecificationHandler.load(project: project, context: @ctx)
31
35
  register_if_necessary(project: project, args: args, name: name)
32
36
 
@@ -43,6 +47,15 @@ module Extension
43
47
  end
44
48
  end
45
49
 
50
+ def check_registration(registration_id:, context:)
51
+ if !ShopifyCLI::Environment.interactive? && (!registration_id || registration_id.empty?)
52
+ message = context.message("errors.missing_push_options_ci", "--registration-id")
53
+ message += context.message("errors.missing_push_options_ci_solution", ShopifyCLI::TOOL_NAME)
54
+ raise ShopifyCLI::Abort,
55
+ message
56
+ end
57
+ end
58
+
46
59
  def self.help
47
60
  ShopifyCLI::Context.new.message("push.help", ShopifyCLI::TOOL_NAME)
48
61
  end
@@ -9,20 +9,40 @@ module Extension
9
9
  "SHOPIFY_API_SECRET" => api_secret,
10
10
  "EXTENSION_ID" => registration_id,
11
11
  }.compact
12
- env =
13
- begin
14
- ShopifyCLI::Resources::EnvFile.read(directory, overrides: env_overrides)
15
- rescue Errno::ENOENT
16
- ShopifyCLI::Resources::EnvFile.from_hash(env_overrides)
17
- end
12
+ env_file_present = env_file_exists?(directory)
13
+ env = if env_file_present
14
+ ShopifyCLI::Resources::EnvFile.read(directory, overrides: env_overrides)
15
+ else
16
+ ShopifyCLI::Resources::EnvFile.from_hash(env_overrides)
17
+ end
18
18
  # This is a somewhat uncomfortable hack we use because `Project::at` is
19
19
  # a global cache and we can't rely on this class loading the project
20
20
  # first. Long-term we should move away from that global cache.
21
21
  project = ExtensionProject.at(directory)
22
22
  project.env = env
23
23
  project
24
- rescue SmartProperties::InitializationError, SmartProperties::MissingValueError
25
- context.abort(context.message("errors.missing_api_key"))
24
+ rescue SmartProperties::InitializationError, SmartProperties::MissingValueError => error
25
+ handle_error(error, context: context)
26
+ end
27
+
28
+ def self.handle_error(error, context:)
29
+ if ShopifyCLI::Environment.interactive?
30
+ properties_hash = { api_key: "SHOPIFY_API_KEY", secret: "SHOPIFY_API_SECRET" }
31
+ missing_env_variables = error.properties.map { |p| properties_hash[p.name] }.compact.join(", ")
32
+ message = context.message("errors.missing_env_file_variables", missing_env_variables)
33
+ message += context.message("errors.missing_env_file_variables_solution", ShopifyCLI::TOOL_NAME)
34
+ else
35
+ properties_hash = { api_key: "--api-key", secret: "--api-secret" }
36
+ missing_options = error.properties.map { |p| properties_hash[p.name] }.compact.join(", ")
37
+ message = context.message("errors.missing_push_options_ci", missing_options)
38
+ message += context.message("errors.missing_push_options_ci_solution", ShopifyCLI::TOOL_NAME)
39
+ end
40
+ raise ShopifyCLI::Abort,
41
+ message
42
+ end
43
+
44
+ def self.env_file_exists?(directory)
45
+ File.exist?(ShopifyCLI::Resources::EnvFile.path(directory))
26
46
  end
27
47
  end
28
48
  end
@@ -174,9 +174,17 @@ module Extension
174
174
  },
175
175
  },
176
176
  errors: {
177
- unknown_type: "Unknown extension type %s",
177
+ unknown_type: "Unknown extension type %s. Valid extension types include: CHECKOUT_POST_PURCHASE, " \
178
+ "CHECKOUT_UI_EXTENSION, THEME_APP_EXTENSION, and PRODUCT_SUBSCRIPTION.",
178
179
  package_not_found: "`%s` package not found.",
179
- missing_api_key: "Missing api_key.",
180
+ missing_push_options_ci: "The following are missing: %s. ",
181
+ missing_push_options_ci_solution: "To add them to a CI environment:\n\t1. Run a connect command " \
182
+ "({{command:%1$s extension connect}})\n\t2. Navigate to the .env file at the root of your project\n\t" \
183
+ "3. Copy the missing values and pass them through as arguments in {{command:%1$s extension push}}",
184
+ missing_env_file_variables: "The following are missing in the .env file: %s. ",
185
+ missing_env_file_variables_solution: "To add it, connect your extension with " \
186
+ "{{command:%1$s extension connect}} " \
187
+ "or run {{command:%1$s extension register}} to register a new extension.",
180
188
  module_not_found: "Unable to find module %s. Ensure your dependencies are up-to-date and try again.",
181
189
  },
182
190
  warnings: {
@@ -47,7 +47,7 @@ module Script
47
47
  private
48
48
 
49
49
  def install_dependencies(ctx, language, script_name, project_creator)
50
- task_runner = Infrastructure::Languages::TaskRunner.for(ctx, language, script_name)
50
+ task_runner = Infrastructure::Languages::TaskRunner.for(ctx, language)
51
51
  CLI::UI::Frame.open(ctx.message(
52
52
  "core.git.pulling_from_to",
53
53
  project_creator.sparse_checkout_repo,
@@ -10,7 +10,7 @@ module Script
10
10
  script_project = script_project_repo.get
11
11
  script_project.env = project.env
12
12
  task_runner = Infrastructure::Languages::TaskRunner
13
- .for(ctx, script_project.language, script_project.script_name)
13
+ .for(ctx, script_project.language)
14
14
 
15
15
  extension_point = ExtensionPoints.get(type: script_project.extension_point_type)
16
16
 
@@ -4,18 +4,11 @@ module Script
4
4
  module Layers
5
5
  module Infrastructure
6
6
  module Languages
7
- class AssemblyScriptTaskRunner
7
+ class AssemblyScriptTaskRunner < TaskRunner
8
8
  BYTECODE_FILE = "build/script.wasm"
9
9
  METADATA_FILE = "build/metadata.json"
10
10
  SCRIPT_SDK_BUILD = "npm run build"
11
11
 
12
- attr_reader :ctx, :script_name
13
-
14
- def initialize(ctx, script_name)
15
- @ctx = ctx
16
- @script_name = script_name
17
- end
18
-
19
12
  def build
20
13
  compile
21
14
  bytecode
@@ -5,15 +5,41 @@ module Script
5
5
  module Infrastructure
6
6
  module Languages
7
7
  class TaskRunner
8
- TASK_RUNNERS = {
9
- "assemblyscript" => AssemblyScriptTaskRunner,
10
- "typescript" => TypeScriptTaskRunner,
11
- "wasm" => WasmTaskRunner,
12
- }
13
-
14
- def self.for(ctx, language, script_name)
15
- raise Errors::TaskRunnerNotFoundError unless TASK_RUNNERS[language]
16
- TASK_RUNNERS[language].new(ctx, script_name)
8
+ attr_reader :ctx
9
+
10
+ def self.for(ctx, language)
11
+ task_runners = {
12
+ "assemblyscript" => AssemblyScriptTaskRunner,
13
+ "typescript" => TypeScriptTaskRunner,
14
+ "wasm" => WasmTaskRunner,
15
+ }
16
+
17
+ raise Errors::TaskRunnerNotFoundError unless task_runners[language]
18
+ task_runners[language].new(ctx)
19
+ end
20
+
21
+ def initialize(ctx)
22
+ @ctx = ctx
23
+ end
24
+
25
+ def build
26
+ raise NotImplementedError
27
+ end
28
+
29
+ def dependencies_installed?
30
+ raise NotImplementedError
31
+ end
32
+
33
+ def install_dependencies
34
+ raise NotImplementedError
35
+ end
36
+
37
+ def metadata_file_location
38
+ raise NotImplementedError
39
+ end
40
+
41
+ def library_version(_library_name)
42
+ raise NotImplementedError
17
43
  end
18
44
  end
19
45
  end
@@ -4,19 +4,12 @@ module Script
4
4
  module Layers
5
5
  module Infrastructure
6
6
  module Languages
7
- class TypeScriptTaskRunner
7
+ class TypeScriptTaskRunner < TaskRunner
8
8
  BYTECODE_FILE = "build/index.wasm"
9
9
  METADATA_FILE = "build/metadata.json"
10
10
  SCRIPT_SDK_BUILD = "npm run build"
11
11
  GEN_METADATA = "npm run gen-metadata"
12
12
 
13
- attr_reader :ctx, :script_name
14
-
15
- def initialize(ctx, script_name)
16
- @ctx = ctx
17
- @script_name = script_name
18
- end
19
-
20
13
  def build
21
14
  compile
22
15
  bytecode
@@ -4,19 +4,15 @@ module Script
4
4
  module Layers
5
5
  module Infrastructure
6
6
  module Languages
7
- class WasmTaskRunner
7
+ class WasmTaskRunner < TaskRunner
8
8
  BYTECODE_FILE = "script.wasm"
9
- attr_reader :ctx, :script_name
10
-
11
- def initialize(ctx, script_name)
12
- @ctx = ctx
13
- @script_name = script_name
14
- end
15
9
 
16
10
  def dependencies_installed?
17
11
  true
18
12
  end
19
13
 
14
+ def install_dependencies; end
15
+
20
16
  def library_version(_library_name)
21
17
  nil
22
18
  end
@@ -149,12 +149,12 @@ module Script
149
149
 
150
150
  language_library_for_api_not_found_cause: "Script can’t be pushed because the %{language} library for API %{api} is missing.",
151
151
  language_library_for_api_not_found_help: "Make sure extension_point.yml contains the correct API library.",
152
- no_scripts_found_in_app: "The selected apps have no scripts. Create them first on the partners' dashboard.",
153
- missing_env_file_variables: "The following variables are missing in the .env file: %s."\
154
- " This can occur when the script hasn't been connected to an app."\
155
- " To connect the script to an app, run {{command:%s script connect}}",
156
- missing_push_options: "The following options are missing from .env: %s."\
157
- " Run {{command:%s script connect}} to connect the script to an app and generate these options.",
152
+ 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.",
158
158
  },
159
159
 
160
160
  create: {
@@ -4,7 +4,6 @@ require "theme_check"
4
4
  module Theme
5
5
  class Command
6
6
  class Check < ShopifyCLI::Command::SubCommand
7
- recommend_default_node_range
8
7
  recommend_default_ruby_range
9
8
 
10
9
  class Options < ShopifyCLI::Options
@@ -5,7 +5,6 @@ require "shopify_cli/theme/development_theme"
5
5
  module Theme
6
6
  class Command
7
7
  class Delete < ShopifyCLI::Command::SubCommand
8
- recommend_default_node_range
9
8
  recommend_default_ruby_range
10
9
 
11
10
  options do |parser, flags|
@@ -3,7 +3,6 @@
3
3
  module Theme
4
4
  class Command
5
5
  class Init < ShopifyCLI::Command::SubCommand
6
- recommend_default_node_range
7
6
  recommend_default_ruby_range
8
7
 
9
8
  options do |parser, flags|
@@ -4,7 +4,6 @@ require "theme_check"
4
4
  module Theme
5
5
  class Command
6
6
  class LanguageServer < ShopifyCLI::Command::SubCommand
7
- recommend_default_node_range
8
7
  recommend_default_ruby_range
9
8
 
10
9
  def call(*)
@@ -5,7 +5,6 @@ require "json"
5
5
  module Theme
6
6
  class Command
7
7
  class Package < ShopifyCLI::Command::SubCommand
8
- recommend_default_node_range
9
8
  recommend_default_ruby_range
10
9
 
11
10
  THEME_DIRECTORIES = %w[
@@ -4,7 +4,6 @@ require "shopify_cli/theme/theme"
4
4
  module Theme
5
5
  class Command
6
6
  class Publish < ShopifyCLI::Command::SubCommand
7
- recommend_default_node_range
8
7
  recommend_default_ruby_range
9
8
 
10
9
  options do |parser, flags|
@@ -7,7 +7,6 @@ require "shopify_cli/theme/syncer"
7
7
  module Theme
8
8
  class Command
9
9
  class Pull < ShopifyCLI::Command::SubCommand
10
- recommend_default_node_range
11
10
  recommend_default_ruby_range
12
11
 
13
12
  options do |parser, flags|
@@ -8,7 +8,6 @@ require "shopify_cli/theme/syncer"
8
8
  module Theme
9
9
  class Command
10
10
  class Push < ShopifyCLI::Command::SubCommand
11
- recommend_default_node_range
12
11
  recommend_default_ruby_range
13
12
 
14
13
  options do |parser, flags|
@@ -4,7 +4,6 @@ require "shopify_cli/theme/dev_server"
4
4
  module Theme
5
5
  class Command
6
6
  class Serve < ShopifyCLI::Command::SubCommand
7
- recommend_default_node_range
8
7
  recommend_default_ruby_range
9
8
 
10
9
  DEFAULT_HTTP_HOST = "127.0.0.1"
@@ -100,14 +100,20 @@ module ShopifyCLI
100
100
  end
101
101
 
102
102
  def check_node_version
103
+ context = Context.new
104
+ if @compatible_node_range && context.which("node").nil?
105
+ raise ShopifyCLI::Abort, context.message("core.errors.missing_node")
106
+ end
107
+
103
108
  check_version(
104
109
  Environment.node_version,
105
110
  range: @compatible_node_range,
106
- runtime: "Node"
111
+ runtime: "Node",
112
+ context: context
107
113
  )
108
114
  end
109
115
 
110
- def check_version(version, range:, runtime:)
116
+ def check_version(version, range:, runtime:, context: Context.new)
111
117
  return if Environment.test?
112
118
  return if range.nil?
113
119
 
@@ -116,7 +122,7 @@ module ShopifyCLI
116
122
  is_lower_than_top = version_without_pre_nor_build < Utilities.version_dropping_pre_and_build(range.to)
117
123
  return if is_higher_than_bottom && is_lower_than_top
118
124
 
119
- Context.new.warn("Your environment #{runtime} version, #{version},"\
125
+ context.warn("Your environment #{runtime} version, #{version},"\
120
126
  " is outside of the range supported by the CLI,"\
121
127
  " #{range.from}..<#{range.to},"\
122
128
  " and might cause incompatibility issues.")
@@ -5,8 +5,8 @@ module ShopifyCLI
5
5
  class Rails < ShopifyCLI::Command::AppSubCommand
6
6
  prerequisite_task :ensure_authenticated
7
7
 
8
- recommend_default_node_range
9
8
  recommend_default_ruby_range
9
+ recommend_default_node_range
10
10
 
11
11
  options do |parser, flags|
12
12
  parser.on("--name=NAME") { |t| flags[:name] = t }
@@ -6,9 +6,6 @@ module ShopifyCLI
6
6
  subcommand :PHP, "php", "shopify_cli/commands/app/create/php"
7
7
  subcommand :Node, "node", "shopify_cli/commands/app/create/node"
8
8
 
9
- recommend_default_node_range
10
- recommend_default_ruby_range
11
-
12
9
  def call(_args, _command_name)
13
10
  @ctx.puts(self.class.help)
14
11
  end
@@ -4,7 +4,6 @@ module ShopifyCLI
4
4
  class Deploy < ShopifyCLI::Command::AppSubCommand
5
5
  subcommand :Heroku, "heroku", "shopify_cli/commands/app/deploy/heroku"
6
6
 
7
- recommend_default_node_range
8
7
  recommend_default_ruby_range
9
8
 
10
9
  def call(args, _name)
@@ -7,7 +7,6 @@ module ShopifyCLI
7
7
  prerequisite_task :ensure_env, :ensure_dev_store
8
8
 
9
9
  recommend_default_ruby_range
10
- recommend_default_node_range
11
10
 
12
11
  options do |parser, flags|
13
12
  parser.on("--host=HOST") do |h|
@@ -38,7 +38,7 @@ module ShopifyCLI
38
38
 
39
39
  # When true the CLI points to spin instances of services
40
40
  SPIN = "SPIN"
41
- INFER_SPIN = "INFER_SPIN"
41
+ SPIN_INSTANCE = "SPIN_INSTANCE"
42
42
  SPIN_WORKSPACE = "SPIN_WORKSPACE"
43
43
  SPIN_NAMESPACE = "SPIN_NAMESPACE"
44
44
  SPIN_HOST = "SPIN_HOST"
@@ -5,6 +5,11 @@ module ShopifyCLI
5
5
  # the environment in which the CLI runs
6
6
  module Environment
7
7
  TRUTHY_ENV_VARIABLE_VALUES = ["1", "true", "TRUE", "yes", "YES"]
8
+ SPIN_OVERRIDE_ENV_NAMES = [
9
+ Constants::EnvironmentVariables::SPIN_WORKSPACE,
10
+ Constants::EnvironmentVariables::SPIN_NAMESPACE,
11
+ Constants::EnvironmentVariables::SPIN_HOST,
12
+ ]
8
13
 
9
14
  def self.ruby_version(context: Context.new)
10
15
  out, err, stat = context.capture3('ruby -e "puts RUBY_VERSION"')
@@ -87,6 +92,20 @@ module ShopifyCLI
87
92
  end
88
93
  end
89
94
 
95
+ def self.spin_url_override(env_variables: ENV)
96
+ tokens = SPIN_OVERRIDE_ENV_NAMES.map do |name|
97
+ env_variables[name]
98
+ end
99
+
100
+ return if tokens.all?(&:nil?)
101
+
102
+ if tokens.any?(&:nil?)
103
+ raise "To manually target a spin instance, you must set #{SPIN_OVERRIDE_ENV_NAMES}"
104
+ else
105
+ tokens.join(".")
106
+ end
107
+ end
108
+
90
109
  def self.use_spin?(env_variables: ENV)
91
110
  env_variable_truthy?(
92
111
  Constants::EnvironmentVariables::SPIN,
@@ -97,32 +116,29 @@ module ShopifyCLI
97
116
  )
98
117
  end
99
118
 
100
- def self.infer_spin?(env_variables: ENV)
101
- env_variable_truthy?(
102
- Constants::EnvironmentVariables::INFER_SPIN,
103
- env_variables: env_variables
104
- )
105
- end
106
-
107
119
  def self.spin_url(env_variables: ENV)
108
- if infer_spin?(env_variables: env_variables)
109
- # TODO: Remove version check and delete spin-legacy branch
110
- # once spin2 becomes the installed "spin" binary by default
111
- spin_version = %x(spin version 2> /dev/null).strip
112
- if spin_version.start_with?("spin-")
113
- # spin2
114
- raise ShopifyCLI:: Abort, "SPIN_INSTANCE must be specified" unless ENV.key?("SPIN_INSTANCE")
115
- %x(spin show -o fqdn 2> /dev/null).strip
116
- else
117
- # spin-legacy
118
- %x(spin info fqdn 2> /dev/null).strip
119
- end
120
+ override = spin_url_override(env_variables: env_variables)
121
+ return override unless override.nil?
122
+
123
+ spin_response = if env_variables.key?(
124
+ Constants::EnvironmentVariables::SPIN_INSTANCE
125
+ )
126
+ spin_show
120
127
  else
121
- spin_workspace = spin_workspace(env_variables: env_variables)
122
- spin_namespace = spin_namespace(env_variables: env_variables)
123
- spin_host = spin_host(env_variables: env_variables)
124
- "#{spin_workspace}.#{spin_namespace}.#{spin_host}"
128
+ spin_show(latest: true)
125
129
  end
130
+
131
+ begin
132
+ instance = JSON.parse(spin_response)
133
+ raise "Missing key 'fqdn' from spin show. Actual response: #{instance}" unless instance.include?("fqdn")
134
+ instance["fqdn"]
135
+ rescue => e
136
+ raise "Failed to infer spin environment from spin show response #{spin_response}: #{e}"
137
+ end
138
+ end
139
+
140
+ def self.spin_show(latest: false)
141
+ latest ? %x(spin show --latest --json) : %x(spin show --json)
126
142
  end
127
143
 
128
144
  def self.send_monorail_events?(env_variables: ENV)
@@ -139,27 +155,5 @@ module ShopifyCLI
139
155
  def self.env_variable_truthy?(variable_name, env_variables: ENV)
140
156
  TRUTHY_ENV_VARIABLE_VALUES.include?(env_variables[variable_name.to_s])
141
157
  end
142
-
143
- def self.spin_workspace(env_variables: ENV)
144
- env_value = env_variables[Constants::EnvironmentVariables::SPIN_WORKSPACE]
145
- return env_value unless env_value.nil?
146
-
147
- if env_value.nil?
148
- raise "No value set for #{Constants::EnvironmentVariables::SPIN_WORKSPACE}"
149
- end
150
- end
151
-
152
- def self.spin_namespace(env_variables: ENV)
153
- env_value = env_variables[Constants::EnvironmentVariables::SPIN_NAMESPACE]
154
- return env_value unless env_value.nil?
155
-
156
- if env_value.nil?
157
- raise "No value set for #{Constants::EnvironmentVariables::SPIN_NAMESPACE}"
158
- end
159
- end
160
-
161
- def self.spin_host(env_variables: ENV)
162
- env_variables[Constants::EnvironmentVariables::SPIN_HOST] || "us.spin.dev"
163
- end
164
158
  end
165
159
  end
@@ -15,6 +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
19
  option_parser: {
19
20
  invalid_option: "The option {{command:%s}} is not supported.",
20
21
  missing_argument: "The required argument {{command:%s}} is missing.",
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyCLI
4
+ module Theme
5
+ module DevServer
6
+ class HotReload
7
+ class RemoteFileReloader
8
+ def initialize(ctx, theme:, streams:)
9
+ @ctx = ctx
10
+ @theme = theme
11
+ @streams = streams
12
+ end
13
+
14
+ def reload(file)
15
+ retries = 6
16
+
17
+ until retries.zero?
18
+ retries -= 1
19
+
20
+ _status, body = fetch_asset(file)
21
+ retries = 0 if updated_file?(body, file)
22
+
23
+ wait
24
+ end
25
+
26
+ notify(file)
27
+ end
28
+
29
+ private
30
+
31
+ def updated_file?(body, file)
32
+ remote_checksum = body.dig("asset", "checksum")
33
+ local_checksum = file.checksum
34
+
35
+ remote_checksum == local_checksum
36
+ end
37
+
38
+ def notify(file)
39
+ @streams.broadcast(JSON.generate(modified: [file]))
40
+ @ctx.debug("[RemoteFileReloader] Modified #{file}")
41
+ end
42
+
43
+ def wait
44
+ sleep(1)
45
+ end
46
+
47
+ def fetch_asset(file)
48
+ ShopifyCLI::AdminAPI.rest_request(
49
+ @ctx,
50
+ shop: @theme.shop,
51
+ path: "themes/#{@theme.id}/assets.json",
52
+ method: "GET",
53
+ api_version: "unstable",
54
+ query: URI.encode_www_form("asset[key]" => file.relative_path.to_s),
55
+ )
56
+ rescue ShopifyCLI::API::APIRequestNotFoundError
57
+ [404, {}]
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "hot_reload/remote_file_reloader"
4
+
3
5
  module ShopifyCLI
4
6
  module Theme
5
7
  module DevServer
@@ -10,6 +12,7 @@ module ShopifyCLI
10
12
  @theme = theme
11
13
  @mode = mode
12
14
  @streams = SSE::Streams.new
15
+ @remote_file_reloader = RemoteFileReloader.new(ctx, theme: @theme, streams: @streams)
13
16
  @watcher = watcher
14
17
  @watcher.add_observer(self, :notify_streams_of_file_change)
15
18
  @ignore_filter = ignore_filter
@@ -32,17 +35,30 @@ module ShopifyCLI
32
35
  end
33
36
 
34
37
  def notify_streams_of_file_change(modified, added, _removed)
35
- files = (modified + added).reject { |file| @ignore_filter&.ignore?(file) }
36
- .map { |file| @theme[file].relative_path }
38
+ files = (modified + added)
39
+ .reject { |file| @ignore_filter&.ignore?(file) }
40
+ .map { |file| @theme[file] }
37
41
 
38
- unless files.empty?
39
- @streams.broadcast(JSON.generate(modified: files))
40
- @ctx.debug("[HotReload] Modified #{files.join(", ")}")
41
- end
42
+ files -= liquid_css_files = files.select(&:liquid_css?)
43
+
44
+ hot_reload(files) unless files.empty?
45
+ remote_reload(liquid_css_files)
42
46
  end
43
47
 
44
48
  private
45
49
 
50
+ def hot_reload(files)
51
+ paths = files.map(&:relative_path)
52
+ @streams.broadcast(JSON.generate(modified: paths))
53
+ @ctx.debug("[HotReload] Modified #{paths.join(", ")}")
54
+ end
55
+
56
+ def remote_reload(files)
57
+ files.each do |file|
58
+ @remote_file_reloader.reload(file)
59
+ end
60
+ end
61
+
46
62
  def request_is_html?(headers)
47
63
  headers["content-type"]&.start_with?("text/html")
48
64
  end
@@ -43,9 +43,8 @@ module ShopifyCLI
43
43
  headers["Accept-Encoding"] = "none"
44
44
  headers["User-Agent"] = "Shopify CLI"
45
45
 
46
- query = URI.decode_www_form(env["QUERY_STRING"]).to_h
46
+ query = URI.decode_www_form(env["QUERY_STRING"])
47
47
  replace_templates = build_replace_templates_param(env)
48
-
49
48
  response = if replace_templates.any?
50
49
  # Pass to SFR the recently modified templates in `replace_templates` body param
51
50
  headers["Authorization"] = "Bearer #{bearer_token}"
@@ -158,7 +157,7 @@ module ShopifyCLI
158
157
  def secure_session_id
159
158
  if secure_session_id_expired?
160
159
  @ctx.debug("Refreshing preview _secure_session_id cookie")
161
- response = request("HEAD", "/", query: { preview_theme_id: @theme.id })
160
+ response = request("HEAD", "/", query: [[:preview_theme_id, @theme.id]])
162
161
  @secure_session_id = extract_secure_session_id_from_response_headers(response)
163
162
  @last_session_cookie_refresh = Time.now
164
163
  end
@@ -189,9 +188,9 @@ module ShopifyCLI
189
188
  response_headers
190
189
  end
191
190
 
192
- def request(method, path, headers: nil, query: {}, form_data: nil, body_stream: nil)
191
+ def request(method, path, headers: nil, query: [], form_data: nil, body_stream: nil)
193
192
  uri = URI.join("https://#{@theme.shop}", path)
194
- uri.query = URI.encode_www_form(query.merge(_fd: 0, pb: 0))
193
+ uri.query = URI.encode_www_form(query + [[:_fd, 0], [:pb, 0]])
195
194
 
196
195
  @ctx.debug("Proxying #{method} #{uri}")
197
196
 
@@ -53,6 +53,10 @@ module ShopifyCLI
53
53
  path.extname == ".liquid"
54
54
  end
55
55
 
56
+ def liquid_css?
57
+ relative_path.to_s.end_with?(".css.liquid")
58
+ end
59
+
56
60
  def json?
57
61
  path.extname == ".json"
58
62
  end
@@ -1,3 +1,3 @@
1
1
  module ShopifyCLI
2
- VERSION = "2.11.0"
2
+ VERSION = "2.11.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shopify-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.11.0
4
+ version: 2.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-07 00:00:00.000000000 Z
11
+ date: 2022-02-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -469,6 +469,7 @@ files:
469
469
  - lib/shopify_cli/theme/dev_server/header_hash.rb
470
470
  - lib/shopify_cli/theme/dev_server/hot-reload.js
471
471
  - lib/shopify_cli/theme/dev_server/hot_reload.rb
472
+ - lib/shopify_cli/theme/dev_server/hot_reload/remote_file_reloader.rb
472
473
  - lib/shopify_cli/theme/dev_server/local_assets.rb
473
474
  - lib/shopify_cli/theme/dev_server/proxy.rb
474
475
  - lib/shopify_cli/theme/dev_server/proxy/template_param_builder.rb