shopify-cli 2.8.0 → 2.10.2

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE.md +18 -0
  3. data/CHANGELOG.md +34 -0
  4. data/Gemfile.lock +1 -1
  5. data/RELEASING.md +4 -3
  6. data/ext/javy/javy.rb +1 -1
  7. data/lib/project_types/extension/commands/push.rb +2 -2
  8. data/lib/project_types/extension/messages/messages.rb +1 -1
  9. data/lib/project_types/extension/models/development_server.rb +2 -4
  10. data/lib/project_types/rails/gem.rb +1 -2
  11. data/lib/project_types/script/cli.rb +10 -0
  12. data/lib/project_types/script/commands/connect.rb +1 -1
  13. data/lib/project_types/script/commands/create.rb +8 -2
  14. data/lib/project_types/script/commands/push.rb +35 -12
  15. data/lib/project_types/script/config/extension_points.yml +12 -0
  16. data/lib/project_types/script/graphql/app_script_set.graphql +2 -0
  17. data/lib/project_types/script/graphql/module_upload_url_generate.graphql +5 -1
  18. data/lib/project_types/script/layers/application/build_script.rb +6 -3
  19. data/lib/project_types/script/layers/application/connect_app.rb +11 -5
  20. data/lib/project_types/script/layers/application/extension_points.rb +50 -26
  21. data/lib/project_types/script/layers/application/project_dependencies.rb +1 -1
  22. data/lib/project_types/script/layers/application/push_script.rb +41 -30
  23. data/lib/project_types/script/layers/domain/errors.rb +10 -3
  24. data/lib/project_types/script/layers/domain/extension_point.rb +16 -2
  25. data/lib/project_types/script/layers/domain/push_package.rb +0 -3
  26. data/lib/project_types/script/layers/domain/script_config.rb +6 -4
  27. data/lib/project_types/script/layers/domain/script_project.rb +1 -0
  28. data/lib/project_types/script/layers/infrastructure/errors.rb +47 -23
  29. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +2 -12
  30. data/lib/project_types/script/layers/infrastructure/languages/project_creator.rb +1 -0
  31. data/lib/project_types/script/layers/infrastructure/languages/task_runner.rb +1 -0
  32. data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +2 -12
  33. data/lib/project_types/script/layers/infrastructure/languages/wasm_project_creator.rb +15 -0
  34. data/lib/project_types/script/layers/infrastructure/languages/wasm_task_runner.rb +36 -0
  35. data/lib/project_types/script/layers/infrastructure/metadata_repository.rb +18 -0
  36. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +7 -8
  37. data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +45 -54
  38. data/lib/project_types/script/layers/infrastructure/script_service.rb +35 -12
  39. data/lib/project_types/script/layers/infrastructure/script_uploader.rb +22 -9
  40. data/lib/project_types/script/loaders/project.rb +44 -0
  41. data/lib/project_types/script/loaders/specification_handler.rb +22 -0
  42. data/lib/project_types/script/messages/messages.rb +38 -19
  43. data/lib/project_types/script/ui/error_handler.rb +52 -30
  44. data/lib/project_types/theme/commands/pull.rb +45 -17
  45. data/lib/project_types/theme/commands/push.rb +62 -27
  46. data/lib/project_types/theme/commands/serve.rb +5 -0
  47. data/lib/project_types/theme/messages/messages.rb +33 -18
  48. data/lib/shopify_cli/constants.rb +7 -2
  49. data/lib/shopify_cli/context.rb +66 -12
  50. data/lib/shopify_cli/core/executor.rb +4 -4
  51. data/lib/shopify_cli/environment.rb +50 -20
  52. data/lib/shopify_cli/identity_auth.rb +4 -3
  53. data/lib/shopify_cli/messages/messages.rb +2 -0
  54. data/lib/shopify_cli/method_object.rb +21 -9
  55. data/lib/shopify_cli/resources/env_file.rb +5 -1
  56. data/lib/shopify_cli/result.rb +61 -59
  57. data/lib/shopify_cli/task.rb +5 -3
  58. data/lib/shopify_cli/theme/dev_server/hot-reload.js +19 -1
  59. data/lib/shopify_cli/theme/dev_server/hot_reload.rb +18 -2
  60. data/lib/shopify_cli/theme/dev_server/proxy.rb +1 -0
  61. data/lib/shopify_cli/theme/dev_server/reload_mode.rb +34 -0
  62. data/lib/shopify_cli/theme/dev_server.rb +6 -21
  63. data/lib/shopify_cli/theme/file.rb +2 -2
  64. data/lib/shopify_cli/theme/filter/path_matcher.rb +38 -0
  65. data/lib/shopify_cli/theme/ignore_filter.rb +14 -18
  66. data/lib/shopify_cli/theme/include_filter.rb +43 -0
  67. data/lib/shopify_cli/theme/syncer.rb +17 -2
  68. data/lib/shopify_cli/theme/theme.rb +26 -4
  69. data/lib/shopify_cli/version.rb +1 -1
  70. data/lib/shopify_cli.rb +6 -1
  71. data/vendor/deps/cli-kit/lib/cli/kit/system.rb +10 -5
  72. data/vendor/deps/cli-ui/lib/cli/ui/os.rb +6 -4
  73. data/vendor/deps/ruby2_keywords/LICENSE +22 -0
  74. data/vendor/deps/ruby2_keywords/README.md +67 -0
  75. data/vendor/deps/ruby2_keywords/Rakefile +54 -0
  76. data/vendor/deps/ruby2_keywords/lib/ruby2_keywords.rb +57 -0
  77. data/vendor/deps/ruby2_keywords/ruby2_keywords.gemspec +18 -0
  78. data/vendor/deps/ruby2_keywords/test/test_keyword.rb +41 -0
  79. metadata +16 -2
@@ -363,70 +363,72 @@ module ShopifyCLI
363
363
  Result::Failure.new(error)
364
364
  end
365
365
 
366
- ##
367
- # takes either a value or a block and chooses the appropriate result
368
- # container based on the type of the value or the type of the block's return
369
- # value. If the type is an exception, it is wrapped in a
370
- # `ShopifyCLI::Result::Failure` and otherwise in a
371
- # `ShopifyCLI::Result::Success`. If a block was provided instead of value, a
372
- # `Proc` is returned and the result wrapping doesn't occur until the block
373
- # is invoked.
374
- #
375
- # #### Parameters
376
- #
377
- # * `*args` should be an `Array` with zero or one element
378
- # * `&block` should be a `Proc` that takes zero or one argument
379
- #
380
- # #### Returns
381
- #
382
- # Returns either a `Result::Success`, `Result::Failure` or a `Proc` that
383
- # produces one of the former when invoked.
384
- #
385
- # #### Examples
386
- #
387
- # Result.wrap(1) # => ShopifyCLI::Result::Success
388
- # Result.wrap(RuntimeError.new) # => ShopifyCLI::Result::Failure
389
- #
390
- # Result.wrap { 1 } # => Proc
391
- # Result.wrap { 1 }.call # => ShopifyCLI::Result::Success
392
- # Result.wrap { raise }.call # => ShopifyCLI::Result::Failure
393
- #
394
- # Result.wrap { |s| s.upcase }.call("hello").tap do |result|
395
- # result # => Result::Success
396
- # result.value # => "HELLO"
397
- # end
398
- #
399
- def self.wrap(*values, &block)
400
- raise ArgumentError, "expected either a value or a block" unless (values.length == 1) ^ block
366
+ class << self
367
+ ##
368
+ # takes either a value or a block and chooses the appropriate result
369
+ # container based on the type of the value or the type of the block's return
370
+ # value. If the type is an exception, it is wrapped in a
371
+ # `ShopifyCli::Result::Failure` and otherwise in a
372
+ # `ShopifyCli::Result::Success`. If a block was provided instead of value, a
373
+ # `Proc` is returned and the result wrapping doesn't occur until the block
374
+ # is invoked.
375
+ #
376
+ # #### Parameters
377
+ #
378
+ # * `*args` should be an `Array` with zero or one element
379
+ # * `&block` should be a `Proc` that takes zero or one argument
380
+ #
381
+ # #### Returns
382
+ #
383
+ # Returns either a `Result::Success`, `Result::Failure` or a `Proc` that
384
+ # produces one of the former when invoked.
385
+ #
386
+ # #### Examples
387
+ #
388
+ # Result.wrap(1) # => ShopifyCli::Result::Success
389
+ # Result.wrap(RuntimeError.new) # => ShopifyCli::Result::Failure
390
+ #
391
+ # Result.wrap { 1 } # => Proc
392
+ # Result.wrap { 1 }.call # => ShopifyCli::Result::Success
393
+ # Result.wrap { raise }.call # => ShopifyCli::Result::Failure
394
+ #
395
+ # Result.wrap { |s| s.upcase }.call("hello").tap do |result|
396
+ # result # => Result::Success
397
+ # result.value # => "HELLO"
398
+ # end
399
+ #
400
+ ruby2_keywords def wrap(*values, &block)
401
+ raise ArgumentError, "expected either a value or a block" unless (values.length == 1) ^ block
401
402
 
402
- if values.length == 1
403
- values.pop.yield_self do |value|
404
- case value
405
- when Result::Success, Result::Failure
406
- value
407
- when NilClass, Exception
408
- Result.failure(value)
409
- else
410
- Result.success(value)
411
- end
412
- end
413
- else
414
- ->(*args) do
415
- begin
416
- wrap(block.call(*args))
417
- rescue Exception => error # rubocop:disable Lint/RescueException
418
- wrap(error)
403
+ if values.length == 1
404
+ values.pop.yield_self do |value|
405
+ case value
406
+ when Result::Success, Result::Failure
407
+ value
408
+ when NilClass, Exception
409
+ Result.failure(value)
410
+ else
411
+ Result.success(value)
412
+ end
419
413
  end
414
+ else
415
+ ->(*args) do
416
+ begin
417
+ wrap(block.call(*args))
418
+ rescue Exception => error # rubocop:disable Lint/RescueException
419
+ wrap(error)
420
+ end
421
+ end.ruby2_keywords
420
422
  end
421
423
  end
422
- end
423
424
 
424
- ##
425
- # Wraps the given block and invokes it with the passed arguments.
426
- #
427
- def self.call(*args, &block)
428
- raise ArgumentError, "expected a block" unless block
429
- wrap(&block).call(*args)
425
+ ##
426
+ # Wraps the given block and invokes it with the passed arguments.
427
+ #
428
+ ruby2_keywords def call(*args, &block)
429
+ raise ArgumentError, "expected a block" unless block
430
+ wrap(&block).call(*args)
431
+ end
430
432
  end
431
433
  end
432
434
  end
@@ -2,9 +2,11 @@ require "shopify_cli"
2
2
 
3
3
  module ShopifyCLI
4
4
  class Task
5
- def self.call(*args, **kwargs)
6
- task = new
7
- task.call(*args, **kwargs)
5
+ class << self
6
+ ruby2_keywords def call(*args)
7
+ task = new
8
+ task.call(*args)
9
+ end
8
10
  end
9
11
 
10
12
  private
@@ -15,9 +15,23 @@
15
15
  eventSource.onerror = () => eventSource.close();
16
16
  }
17
17
 
18
- connect();
18
+ function reloadMode() {
19
+ var namespace = window.__SHOPIFY_CLI_ENV__;
20
+ return namespace.mode;
21
+ }
22
+
23
+ function isFullPageReloadMode(){
24
+ return reloadMode() === "full-page";
25
+ }
26
+
27
+ function isReloadModeActive(){
28
+ return reloadMode() !== "off";
29
+ }
19
30
 
20
31
  function isRefreshRequired(files) {
32
+ if (isFullPageReloadMode()) {
33
+ return true;
34
+ }
21
35
  return files.some((file) => !isCssFile(file) && !isSectionFile(file));
22
36
  }
23
37
 
@@ -119,4 +133,8 @@
119
133
  }
120
134
  }
121
135
  }
136
+
137
+ if (isReloadModeActive()) {
138
+ connect();
139
+ }
122
140
  })();
@@ -4,10 +4,11 @@ module ShopifyCLI
4
4
  module Theme
5
5
  module DevServer
6
6
  class HotReload
7
- def initialize(ctx, app, theme:, watcher:, ignore_filter: nil)
7
+ def initialize(ctx, app, theme:, watcher:, mode:, ignore_filter: nil)
8
8
  @ctx = ctx
9
9
  @app = app
10
10
  @theme = theme
11
+ @mode = mode
11
12
  @streams = SSE::Streams.new
12
13
  @watcher = watcher
13
14
  @watcher.add_observer(self, :notify_streams_of_file_change)
@@ -48,12 +49,27 @@ module ShopifyCLI
48
49
 
49
50
  def inject_hot_reload_javascript(body)
50
51
  hot_reload_js = ::File.read("#{__dir__}/hot-reload.js")
51
- hot_reload_script = "<script>\n#{hot_reload_js}</script>"
52
+ hot_reload_script = [
53
+ "<script>",
54
+ params_js,
55
+ hot_reload_js,
56
+ "</script>",
57
+ ].join("\n")
58
+
52
59
  body = body.join.gsub("</body>", "#{hot_reload_script}\n</body>")
53
60
 
54
61
  [body]
55
62
  end
56
63
 
64
+ def params_js
65
+ env = { mode: @mode }
66
+ <<~JS
67
+ (() => {
68
+ window.__SHOPIFY_CLI_ENV__ = #{env.to_json};
69
+ })();
70
+ JS
71
+ end
72
+
57
73
  def create_stream
58
74
  stream = @streams.new
59
75
 
@@ -18,6 +18,7 @@ module ShopifyCLI
18
18
  "trailer",
19
19
  "transfer-encoding",
20
20
  "upgrade",
21
+ "content-security-policy",
21
22
  ]
22
23
 
23
24
  class Proxy
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyCLI
4
+ module Theme
5
+ module DevServer
6
+ class ReloadMode
7
+ MODES = [:"hot-reload", :"full-page", :off]
8
+
9
+ class << self
10
+ def default
11
+ :"hot-reload"
12
+ end
13
+
14
+ def get!(mode)
15
+ MODES.find { |m| m == mode.to_sym } || raise_error(mode)
16
+ end
17
+
18
+ private
19
+
20
+ def raise_error(invalid_mode)
21
+ error_message = ShopifyCLI::Context.message("theme.serve.reload_mode_is_not_valid", invalid_mode)
22
+ help_message = ShopifyCLI::Context.message("theme.serve.try_a_valid_reload_mode", valid_modes)
23
+
24
+ ShopifyCLI::Context.abort(error_message, help_message)
25
+ end
26
+
27
+ def valid_modes
28
+ MODES.map { |v| "`#{v}`" }.join(", ")
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -6,6 +6,7 @@ require_relative "syncer"
6
6
  require_relative "dev_server/cdn_fonts"
7
7
  require_relative "dev_server/hot_reload"
8
8
  require_relative "dev_server/header_hash"
9
+ require_relative "dev_server/reload_mode"
9
10
  require_relative "dev_server/local_assets"
10
11
  require_relative "dev_server/proxy"
11
12
  require_relative "dev_server/sse"
@@ -25,7 +26,7 @@ module ShopifyCLI
25
26
  class << self
26
27
  attr_accessor :ctx
27
28
 
28
- def start(ctx, root, host: "127.0.0.1", port: 9292, poll: false)
29
+ def start(ctx, root, host: "127.0.0.1", port: 9292, poll: false, mode: ReloadMode.default)
29
30
  @ctx = ctx
30
31
  theme = DevelopmentTheme.new(ctx, root: root)
31
32
  ignore_filter = IgnoreFilter.from_path(root)
@@ -36,7 +37,7 @@ module ShopifyCLI
36
37
  @app = Proxy.new(ctx, theme: theme, syncer: @syncer)
37
38
  @app = CdnFonts.new(@app, theme: theme)
38
39
  @app = LocalAssets.new(ctx, @app, theme: theme)
39
- @app = HotReload.new(ctx, @app, theme: theme, watcher: watcher, ignore_filter: ignore_filter)
40
+ @app = HotReload.new(ctx, @app, theme: theme, watcher: watcher, mode: mode, ignore_filter: ignore_filter)
40
41
  stopped = false
41
42
  address = "http://#{host}:#{port}"
42
43
 
@@ -83,7 +84,9 @@ module ShopifyCLI
83
84
  ShopifyCLI::API::APIRequestUnauthorizedError
84
85
  raise ShopifyCLI::Abort, @ctx.message("theme.serve.ensure_user", theme.shop)
85
86
  rescue Errno::EADDRINUSE
86
- abort_address_already_in_use(address)
87
+ error_message = @ctx.message("theme.serve.address_already_in_use", address)
88
+ help_message = @ctx.message("theme.serve.try_port_option")
89
+ @ctx.abort(error_message, help_message)
87
90
  rescue Errno::EADDRNOTAVAIL
88
91
  raise AddressBindingError, "Error binding to the address #{host}."
89
92
  end
@@ -94,24 +97,6 @@ module ShopifyCLI
94
97
  @syncer.shutdown
95
98
  WebServer.shutdown
96
99
  end
97
-
98
- private
99
-
100
- def abort_address_already_in_use(address)
101
- open_frame(@ctx.message("theme.serve.already_in_use_error"), color: :red) do
102
- @ctx.puts(@ctx.message("theme.serve.address_already_in_use", address))
103
- end
104
-
105
- open_frame(@ctx.message("theme.serve.try_this"), color: :green) do
106
- @ctx.puts(@ctx.message("theme.serve.try_port_option"))
107
- end
108
-
109
- raise ShopifyCLI::AbortSilent
110
- end
111
-
112
- def open_frame(title, color:, &block)
113
- CLI::UI::Frame.open(title, color: CLI::UI.resolve_color(color), timing: false, &block)
114
- end
115
100
  end
116
101
  end
117
102
  end
@@ -18,7 +18,7 @@ module ShopifyCLI
18
18
 
19
19
  def read
20
20
  if text?
21
- path.read
21
+ path.read(universal_newline: true)
22
22
  else
23
23
  path.read(mode: "rb")
24
24
  end
@@ -27,7 +27,7 @@ module ShopifyCLI
27
27
  def write(content)
28
28
  path.parent.mkpath unless path.parent.directory?
29
29
  if text?
30
- path.write(content)
30
+ path.write(content, universal_newline: true)
31
31
  else
32
32
  path.write(content, 0, mode: "wb")
33
33
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShopifyCLI
4
+ module Theme
5
+ module Filter
6
+ module PathMatcher
7
+ def regex_match?(regex, path)
8
+ regex.match?(path)
9
+ rescue StandardError
10
+ false
11
+ end
12
+
13
+ def glob_match?(glob, path)
14
+ !!::File.fnmatch?(glob, path)
15
+ end
16
+
17
+ def regex?(pattern)
18
+ pattern.start_with?("/") && pattern.end_with?("/")
19
+ end
20
+
21
+ def as_regex(pattern)
22
+ Regexp.new(pattern.gsub(%r{^\/|\/$}, ""))
23
+ end
24
+
25
+ def as_glob(pattern)
26
+ # if specifying a directory, match everything below it
27
+ pattern += "*" if pattern.end_with?("/")
28
+
29
+ # The pattern will be scoped to root directory, so it should match anything
30
+ # within that space
31
+ pattern.prepend("*") unless pattern.start_with?("*")
32
+
33
+ pattern
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "filter/path_matcher"
4
+
3
5
  module ShopifyCLI
4
6
  module Theme
5
7
  class IgnoreFilter
8
+ include Filter::PathMatcher
9
+
6
10
  FILE = ".shopifyignore"
7
11
 
8
12
  DEFAULT_REGEXES = [
@@ -72,11 +76,11 @@ module ShopifyCLI
72
76
  return true if path.empty?
73
77
 
74
78
  regexes.each do |regex|
75
- return true if regex.match(path)
79
+ return true if regex_match?(regex, path)
76
80
  end
77
81
 
78
82
  globs.each do |glob|
79
- return true if ::File.fnmatch?(glob, path)
83
+ return true if glob_match?(glob, path)
80
84
  end
81
85
 
82
86
  false
@@ -91,24 +95,16 @@ module ShopifyCLI
91
95
  new_regexes = DEFAULT_REGEXES.dup
92
96
  new_globs = DEFAULT_GLOBS.dup
93
97
 
94
- patterns.each do |pattern|
95
- pattern = pattern.strip
96
-
97
- if pattern.start_with?("/") && pattern.end_with?("/")
98
- new_regexes << Regexp.new(pattern.gsub(%r{^\/|\/$}, ""))
99
- next
98
+ patterns
99
+ .map(&:strip)
100
+ .each do |pattern|
101
+ if regex?(pattern)
102
+ new_regexes << as_regex(pattern)
103
+ else
104
+ new_globs << as_glob(pattern)
105
+ end
100
106
  end
101
107
 
102
- # if specifying a directory, match everything below it
103
- pattern += "*" if pattern.end_with?("/")
104
-
105
- # The pattern will be scoped to root directory, so it should match anything
106
- # within that space
107
- pattern.prepend("*") unless pattern.start_with?("*")
108
-
109
- new_globs << pattern
110
- end
111
-
112
108
  [new_regexes, new_globs]
113
109
  end
114
110
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "filter/path_matcher"
4
+
5
+ module ShopifyCLI
6
+ module Theme
7
+ class IncludeFilter
8
+ include Filter::PathMatcher
9
+
10
+ def initialize(pattern = nil)
11
+ @pattern = pattern
12
+ end
13
+
14
+ def match?(path)
15
+ return true unless present?(@pattern)
16
+
17
+ if regex_pattern?
18
+ regex_match?(regex_pattern, path)
19
+ else
20
+ glob_match?(glob_pattern, path)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def present?(pattern)
27
+ !pattern.nil? && !pattern.empty?
28
+ end
29
+
30
+ def regex_pattern?
31
+ @is_regex_pattern ||= regex?(@pattern)
32
+ end
33
+
34
+ def regex_pattern
35
+ @regex_pattern ||= as_regex(@pattern)
36
+ end
37
+
38
+ def glob_pattern
39
+ @glob_pattern ||= as_glob(@pattern)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -16,13 +16,15 @@ module ShopifyCLI
16
16
  API_VERSION = "unstable"
17
17
 
18
18
  attr_reader :checksums
19
+ attr_accessor :include_filter
19
20
  attr_accessor :ignore_filter
20
21
 
21
22
  def_delegators :@error_reporter, :has_any_error?
22
23
 
23
- def initialize(ctx, theme:, ignore_filter: nil)
24
+ def initialize(ctx, theme:, include_filter: nil, ignore_filter: nil)
24
25
  @ctx = ctx
25
26
  @theme = theme
27
+ @include_filter = include_filter
26
28
  @ignore_filter = ignore_filter
27
29
  @error_reporter = ErrorReporter.new(ctx)
28
30
  @standard_reporter = StandardReporter.new(ctx)
@@ -193,7 +195,7 @@ module ShopifyCLI
193
195
  # Already enqueued
194
196
  return if @pending.include?(operation)
195
197
 
196
- if @ignore_filter&.ignore?(operation.file_path)
198
+ if ignore?(operation)
197
199
  @ctx.debug("ignore #{operation.file_path}")
198
200
  return
199
201
  end
@@ -251,6 +253,19 @@ module ShopifyCLI
251
253
  response
252
254
  end
253
255
 
256
+ def ignore?(operation)
257
+ path = operation.file_path
258
+ ignored_by_ignore_filter?(path) || ignored_by_include_filter?(path)
259
+ end
260
+
261
+ def ignored_by_ignore_filter?(path)
262
+ ignore_filter&.ignore?(path)
263
+ end
264
+
265
+ def ignored_by_include_filter?(path)
266
+ include_filter && !include_filter.match?(path)
267
+ end
268
+
254
269
  def get(file)
255
270
  _status, body, response = ShopifyCLI::AdminAPI.rest_request(
256
271
  @ctx,
@@ -173,15 +173,37 @@ module ShopifyCLI
173
173
  end
174
174
 
175
175
  def live(ctx, root: nil)
176
- _status, body = fetch_themes(ctx)
176
+ find(ctx, root) { |attrs| attrs["role"] == "main" }
177
+ end
177
178
 
178
- body["themes"]
179
- .find { |theme_attrs| theme_attrs["role"] == "main" }
180
- .tap { |theme_attrs| break new(ctx, root: root, **allowed_attrs(theme_attrs)) }
179
+ def development(ctx, root: nil)
180
+ find(ctx, root) { |attrs| attrs["role"] == "development" }
181
+ end
182
+
183
+ # Finds a Theme by its identifier
184
+ #
185
+ # #### Parameters
186
+ # * `ctx` - current running context of your command
187
+ # * `root` - theme root
188
+ # * `identifier` - theme ID or theme name
189
+ def find_by_identifier(ctx, root: nil, identifier:)
190
+ find(ctx, root) do |attrs|
191
+ attrs.slice("name", "id").values.map(&:to_s).include?(identifier)
192
+ end
181
193
  end
182
194
 
183
195
  private
184
196
 
197
+ def find(ctx, root, &block)
198
+ _status, body = fetch_themes(ctx)
199
+
200
+ body["themes"]
201
+ .find(&block)
202
+ .tap do |attrs|
203
+ break new(ctx, root: root, **allowed_attrs(attrs)) if attrs
204
+ end
205
+ end
206
+
185
207
  def allowed_attrs(attrs)
186
208
  attrs.slice("id", "name", "role").transform_keys(&:to_sym)
187
209
  end
@@ -1,3 +1,3 @@
1
1
  module ShopifyCLI
2
- VERSION = "2.8.0"
2
+ VERSION = "2.10.2"
3
3
  end
data/lib/shopify_cli.rb CHANGED
@@ -15,12 +15,13 @@ ENV["PATH"] = ENV["PATH"].split(":").select { |p| p.start_with?("/", "~") }.join
15
15
  vendor_path = File.expand_path("../../vendor/lib", __FILE__)
16
16
  $LOAD_PATH.unshift(vendor_path) unless $LOAD_PATH.include?(vendor_path)
17
17
 
18
- deps = %w(cli-ui cli-kit smart_properties webrick)
18
+ deps = %w(cli-ui cli-kit smart_properties ruby2_keywords webrick)
19
19
  deps.each do |dep|
20
20
  vendor_path = File.expand_path("../../vendor/deps/#{dep}/lib", __FILE__)
21
21
  $LOAD_PATH.unshift(vendor_path) unless $LOAD_PATH.include?(vendor_path)
22
22
  end
23
23
 
24
+ require "ruby2_keywords"
24
25
  require "cli/ui"
25
26
  require "cli/kit"
26
27
  require "smart_properties"
@@ -139,6 +140,10 @@ module ShopifyCLI
139
140
  require "shopify_cli/messages/messages"
140
141
  Context.load_messages(ShopifyCLI::Messages::MESSAGES)
141
142
 
143
+ # cli-ui utilities for capturing the output close the stream while capturing.
144
+ # By setting the value here we persist the tty value for the whole lifetime of the process.
145
+ Environment.interactive = $stdin.tty?
146
+
142
147
  def self.cache_dir
143
148
  cache_dir = if Environment.test?
144
149
  TEMP_DIR
@@ -174,11 +174,16 @@ module CLI
174
174
  end
175
175
 
176
176
  def os
177
- return :mac if /darwin/.match(RUBY_PLATFORM)
178
- return :linux if /linux/.match(RUBY_PLATFORM)
179
- return :windows if /mingw32/.match(RUBY_PLATFORM)
180
-
181
- raise "Could not determine OS from platform #{RUBY_PLATFORM}"
177
+ @current_os ||= case RbConfig::CONFIG['host_os']
178
+ when /darwin/ then :mac
179
+ when /linux/ then :linux
180
+ else
181
+ if RUBY_PLATFORM !~ /cygwin/ && ENV['OS'] == 'Windows_NT'
182
+ :windows
183
+ else
184
+ raise "Could not determine OS from host_os #{RbConfig::CONFIG["host_os"]}"
185
+ end
186
+ end
182
187
  end
183
188
 
184
189
  private
@@ -4,15 +4,17 @@ module CLI
4
4
  # Determines which OS is currently running the UI, to make it easier to
5
5
  # adapt its behaviour to the features of the OS.
6
6
  def self.current
7
- @current_os ||= case RUBY_PLATFORM
7
+ @current_os ||= case RbConfig::CONFIG['host_os']
8
8
  when /darwin/
9
9
  Mac
10
10
  when /linux/
11
11
  Linux
12
- when /mingw32/
13
- Windows
14
12
  else
15
- raise "Could not determine OS from platform #{RUBY_PLATFORM}"
13
+ if RUBY_PLATFORM !~ /cygwin/ && ENV['OS'] == 'Windows_NT'
14
+ Windows
15
+ else
16
+ raise "Could not determine OS from host_os #{RbConfig::CONFIG["host_os"]}"
17
+ end
16
18
  end
17
19
  end
18
20